noinst_LTLIBRARIES += libostree.la
-libostree_la_SOURCES = libostree/ostree.h \
- libostree/ostree-core.c \
- libostree/ostree-core.h \
- libostree/ostree-repo.c \
- libostree/ostree-repo.h \
- libostree/ostree-repo-file.c \
- libostree/ostree-repo-file.h \
- libostree/ostree-repo-file-enumerator.c \
- libostree/ostree-repo-file-enumerator.h \
- libostree/ostree-checkout.c \
- libostree/ostree-checkout.h \
+libostree_la_SOURCES = src/libostree/ostree.h \
+ src/libostree/ostree-core.c \
+ src/libostree/ostree-core.h \
+ src/libostree/ostree-repo.c \
+ src/libostree/ostree-repo.h \
+ src/libostree/ostree-repo-file.c \
+ src/libostree/ostree-repo-file.h \
+ src/libostree/ostree-repo-file-enumerator.c \
+ src/libostree/ostree-repo-file-enumerator.h \
+ src/libostree/ostree-checkout.c \
+ src/libostree/ostree-checkout.h \
$(NULL)
-libostree_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -I$(srcdir)/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+libostree_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
-bin_SCRIPTS += osbuild/ostree-buildone \
- osbuild/ostree-buildone-make \
- osbuild/ostree-buildone-makeinstall-split-artifacts \
+bin_SCRIPTS += src/osbuild/ostree-buildone \
+ src/osbuild/ostree-buildone-make \
+ src/osbuild/ostree-buildone-makeinstall-split-artifacts \
$(NULL)
bin_PROGRAMS += ostree
-ostree_SOURCES = ostree/main.c \
- ostree/ot-builtins.h \
- ostree/ot-builtin-checkout.c \
- ostree/ot-builtin-commit.c \
- ostree/ot-builtin-compose.c \
- ostree/ot-builtin-diff.c \
- ostree/ot-builtin-fsck.c \
- ostree/ot-builtin-init.c \
- ostree/ot-builtin-log.c \
- ostree/ot-builtin-run-triggers.c \
- ostree/ot-builtin-remote.c \
- ostree/ot-builtin-rev-parse.c \
- ostree/ot-builtin-show.c \
+ostree_SOURCES = src/ostree/main.c \
+ src/ostree/ot-builtins.h \
+ src/ostree/ot-builtin-checkout.c \
+ src/ostree/ot-builtin-commit.c \
+ src/ostree/ot-builtin-compose.c \
+ src/ostree/ot-builtin-diff.c \
+ src/ostree/ot-builtin-fsck.c \
+ src/ostree/ot-builtin-init.c \
+ src/ostree/ot-builtin-log.c \
+ src/ostree/ot-builtin-run-triggers.c \
+ src/ostree/ot-builtin-remote.c \
+ src/ostree/ot-builtin-rev-parse.c \
+ src/ostree/ot-builtin-show.c \
$(NULL)
if USE_LIBSOUP_GNOME
-ostree_SOURCES += ostree/ot-builtin-pull.c
+ostree_SOURCES += src/ostree/ot-builtin-pull.c
endif
-ostree_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -I$(srcdir)/libostree -I$(srcdir)/ostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
noinst_LTLIBRARIES += libotutil.la
libotutil_la_SOURCES = \
- libotutil/ot-opt-utils.c \
- libotutil/ot-opt-utils.h \
- libotutil/ot-unix-utils.c \
- libotutil/ot-unix-utils.h \
- libotutil/ot-variant-utils.c \
- libotutil/ot-variant-utils.h \
- libotutil/ot-gio-utils.c \
- libotutil/ot-gio-utils.h \
- libotutil/ot-glib-compat.c \
- libotutil/ot-glib-compat.h \
- libotutil/otutil.h \
+ src/libotutil/ot-opt-utils.c \
+ src/libotutil/ot-opt-utils.h \
+ src/libotutil/ot-unix-utils.c \
+ src/libotutil/ot-unix-utils.h \
+ src/libotutil/ot-variant-utils.c \
+ src/libotutil/ot-variant-utils.h \
+ src/libotutil/ot-gio-utils.c \
+ src/libotutil/ot-gio-utils.h \
+ src/libotutil/ot-glib-compat.c \
+ src/libotutil/ot-glib-compat.h \
+ src/libotutil/otutil.h \
$(NULL)
-libotutil_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
+libotutil_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-#include <gio/gunixinputstream.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-gboolean
-ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
-{
- GFile *dir;
- GError *temp_error = NULL;
- gboolean ret = FALSE;
-
- dir = g_file_new_for_path (path);
- if (with_parents)
- ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
- else
- ret = g_file_make_directory (dir, NULL, &temp_error);
- if (!ret)
- {
- if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- else
- g_clear_error (&temp_error);
- }
-
- ret = TRUE;
- out:
- g_clear_object (&dir);
- return ret;
-}
-
-
-char *
-ot_util_get_file_contents_utf8 (const char *path,
- GError **error)
-{
- GFile *file = NULL;
- char *ret = NULL;
-
- file = ot_util_new_file_for_path (path);
- if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
- goto out;
-
- out:
- g_clear_object (&file);
- return ret;
-}
-
-gboolean
-ot_util_gfile_load_contents_utf8 (GFile *file,
- GCancellable *cancellable,
- char **contents_out,
- char **etag_out,
- GError **error)
-{
- char *ret_contents = NULL;
- char *ret_etag = NULL;
- gsize len;
- gboolean ret = FALSE;
-
- if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
- goto out;
- if (!g_utf8_validate (ret_contents, len, NULL))
- {
- g_set_error (error,
- G_IO_ERROR,
- G_IO_ERROR_INVALID_DATA,
- "Invalid UTF-8");
- goto out;
- }
-
- if (contents_out)
- *contents_out = ret_contents;
- else
- g_free (ret_contents);
- ret_contents = NULL;
- if (etag_out)
- *etag_out = ret_etag;
- else
- g_free (ret_etag);
- ret_etag = NULL;
- ret = TRUE;
- out:
- g_free (ret_contents);
- g_free (ret_etag);
- return ret;
-}
-
-GInputStream *
-ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
-{
- GInputStream *ret = NULL;
- int fd;
- int flags = O_RDONLY;
- char *path = NULL;
-
- path = g_file_get_path (file);
-#ifdef O_NOATIME
- flags |= O_NOATIME;
-#endif
- fd = open (path, flags);
- if (fd < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
-
- out:
- g_free (path);
- return ret;
-}
-
-/* Like g_file_new_for_path, but only do local stuff, not GVFS */
-GFile *
-ot_util_new_file_for_path (const char *path)
-{
- return g_vfs_get_file_for_path (g_vfs_get_local (), path);
-}
-
-const char *
-ot_gfile_get_path_cached (GFile *file)
-{
- const char *path;
-
- path = g_object_get_data ((GObject*)file, "ostree-file-path");
- if (!path)
- {
- path = g_file_get_path (file);
- g_object_set_data_full ((GObject*)file, "ostree-file-path", (char*)path, (GDestroyNotify)g_free);
- }
- return path;
-}
-
-
-const char *
-ot_gfile_get_basename_cached (GFile *file)
-{
- const char *name;
-
- name = g_object_get_data ((GObject*)file, "ostree-file-name");
- if (!name)
- {
- name = g_file_get_basename (file);
- g_object_set_data_full ((GObject*)file, "ostree-file-name", (char*)name, (GDestroyNotify)g_free);
- }
- return name;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GIO_UTILS_H__
-#define __OSTREE_GIO_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-GFile *ot_util_new_file_for_path (const char *path);
-
-const char *ot_gfile_get_path_cached (GFile *file);
-
-const char *ot_gfile_get_basename_cached (GFile *file);
-
-gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
-
-char * ot_util_get_file_contents_utf8 (const char *path, GError **error);
-
-gboolean ot_util_gfile_load_contents_utf8 (GFile *file,
- GCancellable *cancellable,
- char **contents_out,
- char **etag_out,
- GError **error);
-
-GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-#if GLIB_CHECK_VERSION(2,32,0)
-/* nothing */
-#else
-/* Code copied from glib/glib/genviron.c */
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-static gint
-ot_g_environ_find (gchar **envp,
- const gchar *variable)
-{
- gint len, i;
-
- len = strlen (variable);
-
- for (i = 0; envp[i]; i++)
- {
- if (strncmp (envp[i], variable, len) == 0 &&
- envp[i][len] == '=')
- return i;
- }
-
- return -1;
-}
-
-const gchar *
-ot_g_environ_getenv (gchar **envp,
- const gchar *variable)
-{
- gint index;
-
- g_return_val_if_fail (envp != NULL, NULL);
- g_return_val_if_fail (variable != NULL, NULL);
-
- index = ot_g_environ_find (envp, variable);
- if (index != -1)
- return envp[index] + strlen (variable) + 1;
- else
- return NULL;
-}
-
-gchar **
-ot_g_environ_setenv (gchar **envp,
- const gchar *variable,
- const gchar *value,
- gboolean overwrite)
-{
- gint index;
-
- g_return_val_if_fail (envp != NULL, NULL);
- g_return_val_if_fail (variable != NULL, NULL);
- g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
-
- index = ot_g_environ_find (envp, variable);
- if (index != -1)
- {
- if (overwrite)
- {
- g_free (envp[index]);
- envp[index] = g_strdup_printf ("%s=%s", variable, value);
- }
- }
- else
- {
- gint length;
-
- length = g_strv_length (envp);
- envp = g_renew (gchar *, envp, length + 2);
- envp[length] = g_strdup_printf ("%s=%s", variable, value);
- envp[length + 1] = NULL;
- }
-
- return envp;
-}
-
-gchar **
-ot_g_environ_unsetenv (gchar **envp,
- const gchar *variable)
-{
- gint len;
- gchar **e, **f;
-
- g_return_val_if_fail (envp != NULL, NULL);
- g_return_val_if_fail (variable != NULL, NULL);
- g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
-
- len = strlen (variable);
-
- /* Note that we remove *all* environment entries for
- * the variable name, not just the first.
- */
- e = f = envp;
- while (*e != NULL)
- {
- if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
- {
- *f = *e;
- f++;
- }
- e++;
- }
- *f = NULL;
-
- return envp;
-}
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GLIB_COMPAT_H__
-#define __OSTREE_GLIB_COMPAT_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#if GLIB_CHECK_VERSION(2,32,0)
-#define ot_g_environ_getenv g_environ_getenv
-#define ot_g_environ_setenv g_environ_setenv
-#define ot_g_environ_unsetenv g_environ_unsetenv
-#else
-const gchar *
-ot_g_environ_getenv (gchar **envp,
- const gchar *variable);
-
-gchar **
-ot_g_environ_setenv (gchar **envp,
- const gchar *variable,
- const gchar *value,
- gboolean overwrite);
-
-gchar **
-ot_g_environ_unsetenv (gchar **envp,
- const gchar *variable);
-#endif
-
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-void
-ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
-{
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_OPT_UTILS_H__
-#define __OSTREE_OPT_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-unix-utils.h"
-
-#include <gio/gio.h>
-#include <gio/gunixoutputstream.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <dirent.h>
-
-gboolean
-ot_util_spawn_pager (GOutputStream **out_stream,
- GError **error)
-{
- const char *pager;
- char *argv[2];
- int stdin_fd;
- pid_t pid;
- gboolean ret = FALSE;
- GOutputStream *ret_stream = NULL;
-
- if (!isatty (1))
- {
- ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
- }
- else
- {
- pager = g_getenv ("GIT_PAGER");
- if (pager == NULL)
- pager = "less";
-
- argv[0] = (char*)pager;
- argv[1] = NULL;
-
- if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
- NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
- {
- g_prefix_error (error, "%s", "Failed to spawn pager: ");
- goto out;
- }
-
- ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
- }
-
- *out_stream = ret_stream;
- ret_stream = NULL;
- ret = TRUE;
- out:
- g_clear_object (&ret_stream);
- return ret;
-}
-
-static int
-compare_filenames_by_component_length (const char *a,
- const char *b)
-{
- char *a_slash, *b_slash;
-
- a_slash = strchr (a, '/');
- b_slash = strchr (b, '/');
- while (a_slash && b_slash)
- {
- a = a_slash + 1;
- b = b_slash + 1;
- a_slash = strchr (a, '/');
- b_slash = strchr (b, '/');
- }
- if (a_slash)
- return -1;
- else if (b_slash)
- return 1;
- else
- return 0;
-}
-
-GPtrArray *
-ot_util_sort_filenames_by_component_length (GPtrArray *files)
-{
- GPtrArray *array = g_ptr_array_sized_new (files->len);
- memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
- g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
- return array;
-}
-
-int
-ot_util_count_filename_components (const char *path)
-{
- int i = 0;
-
- while (path)
- {
- i++;
- path = strchr (path, '/');
- if (path)
- path++;
- }
- return i;
-}
-
-gboolean
-ot_util_filename_has_dotdot (const char *path)
-{
- char *p;
- char last;
-
- if (strcmp (path, "..") == 0)
- return TRUE;
- if (g_str_has_prefix (path, "../"))
- return TRUE;
- p = strstr (path, "/..");
- if (!p)
- return FALSE;
- last = *(p + 1);
- return last == '\0' || last == '/';
-}
-
-GPtrArray *
-ot_util_path_split (const char *path)
-{
- GPtrArray *ret = NULL;
- const char *p;
- const char *slash;
- int i;
-
- g_return_val_if_fail (path[0] != '/', NULL);
-
- ret = g_ptr_array_new ();
- g_ptr_array_set_free_func (ret, g_free);
-
- p = path;
- do {
- slash = strchr (p, '/');
- if (!slash)
- {
- g_ptr_array_add (ret, g_strdup (p));
- p = NULL;
- }
- else
- {
- g_ptr_array_add (ret, g_strndup (p, slash - p));
- p = slash + 1;
- }
- } while (p && *p);
-
- /* Canonicalize by removing duplicate '.' */
- for (i = ret->len-1; i >= 0; i--)
- {
- if (strcmp (ret->pdata[i], ".") == 0)
- g_ptr_array_remove_index (ret, i);
- }
-
- return ret;
-}
-
-char *
-ot_util_path_join_n (const char *base, GPtrArray *components, int n)
-{
- int max = MIN(n+1, components->len);
- GPtrArray *subcomponents;
- char *path;
- int i;
-
- subcomponents = g_ptr_array_new ();
-
- if (base != NULL)
- g_ptr_array_add (subcomponents, (char*)base);
-
- for (i = 0; i < max; i++)
- {
- g_ptr_array_add (subcomponents, components->pdata[i]);
- }
- g_ptr_array_add (subcomponents, NULL);
-
- path = g_build_filenamev ((char**)subcomponents->pdata);
- g_ptr_array_free (subcomponents, TRUE);
-
- return path;
-}
-
-void
-ot_util_set_error_from_errno (GError **error,
- gint saved_errno)
-{
- g_set_error_literal (error,
- G_IO_ERROR,
- 0,
- g_strerror (saved_errno));
- errno = saved_errno;
-}
-
-int
-ot_util_open_file_read (const char *path, GError **error)
-{
- char *dirname = NULL;
- char *basename = NULL;
- DIR *dir = NULL;
- int fd = -1;
-
- dirname = g_path_get_dirname (path);
- basename = g_path_get_basename (path);
- dir = opendir (dirname);
- if (dir == NULL)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
-
- out:
- g_free (basename);
- g_free (dirname);
- if (dir != NULL)
- closedir (dir);
- return fd;
-}
-
-int
-ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
-{
- int fd;
- int flags = O_RDONLY;
-
-#ifdef O_CLOEXEC
- flags |= O_CLOEXEC;
-#endif
-#ifdef O_NOATIME
- flags |= O_NOATIME;
-#endif
- fd = openat (dirfd, name, flags);
- if (fd < 0)
- ot_util_set_error_from_errno (error, errno);
- return fd;
-}
-
-void
-ot_util_fatal_literal (const char *msg)
-{
- g_printerr ("%s\n", msg);
- exit (1);
-}
-
-void
-ot_util_fatal_gerror (GError *error)
-{
- g_assert (error != NULL);
- ot_util_fatal_literal (error->message);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UNIX_UTILS_H__
-#define __OSTREE_UNIX_UTILS_H__
-
-#include <gio/gio.h>
-
-/* I just put all this shit here. Sue me. */
-#include <sys/types.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-G_BEGIN_DECLS
-
-gboolean ot_util_spawn_pager (GOutputStream **out_stream, GError **error);
-
-void ot_util_fatal_literal (const char *msg) G_GNUC_NORETURN;
-
-void ot_util_fatal_gerror (GError *error) G_GNUC_NORETURN;
-
-gboolean ot_util_filename_has_dotdot (const char *path);
-
-GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
-
-GPtrArray* ot_util_path_split (const char *path);
-
-char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
-
-int ot_util_count_filename_components (const char *path);
-
-int ot_util_open_file_read (const char *path, GError **error);
-
-int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
-
-void ot_util_set_error_from_errno (GError **error, gint saved_errno);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-GHashTable *
-ot_util_variant_asv_to_hash_table (GVariant *variant)
-{
- GHashTable *ret;
- GVariantIter *viter;
- char *key;
- GVariant *value;
-
- ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
- viter = g_variant_iter_new (variant);
- while (g_variant_iter_next (viter, "{s@v}", &key, &value))
- g_hash_table_replace (ret, key, g_variant_ref_sink (value));
-
- g_variant_iter_free (viter);
-
- return ret;
-}
-
-gboolean
-ot_util_variant_save (GFile *dest,
- GVariant *variant,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- GOutputStream *out = NULL;
- gsize bytes_written;
-
- out = (GOutputStream*)g_file_replace (dest, NULL, 0, FALSE, cancellable, error);
- if (!out)
- goto out;
-
- if (!g_output_stream_write_all (out,
- g_variant_get_data (variant),
- g_variant_get_size (variant),
- &bytes_written,
- cancellable,
- error))
- goto out;
- if (!g_output_stream_close (out, cancellable, error))
- goto out;
-
- ret = TRUE;
- out:
- g_clear_object (&out);
- return ret;
-}
-
-GVariant *
-ot_util_variant_take_ref (GVariant *variant)
-{
-#if GLIB_CHECK_VERSION(2,32,0)
- return g_variant_take_ref (variant);
-#else
- if (g_variant_is_floating (variant))
- return g_variant_ref_sink (variant);
- return variant;
-#endif
-}
-
-gboolean
-ot_util_variant_map (GFile *src,
- const GVariantType *type,
- GVariant **out_variant,
- GError **error)
-{
- gboolean ret = FALSE;
- GMappedFile *mfile = NULL;
- char *path = NULL;
- GVariant *ret_variant = NULL;
-
- path = g_file_get_path (src);
- mfile = g_mapped_file_new (path, FALSE, error);
- if (!mfile)
- goto out;
-
- ret_variant = g_variant_new_from_data (type,
- g_mapped_file_get_contents (mfile),
- g_mapped_file_get_length (mfile),
- FALSE,
- (GDestroyNotify) g_mapped_file_unref,
- mfile);
- mfile = NULL;
- g_variant_ref_sink (ret_variant);
-
- ret = TRUE;
- *out_variant = ret_variant;
- ret_variant = NULL;
- out:
- if (ret_variant)
- g_variant_unref (ret_variant);
- if (mfile)
- g_mapped_file_unref (mfile);
- g_free (path);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_VARIANT_UTILS_H__
-#define __OSTREE_VARIANT_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-GHashTable *ot_util_variant_asv_to_hash_table (GVariant *variant);
-
-GVariant * ot_util_variant_take_ref (GVariant *variant);
-
-gboolean ot_util_variant_save (GFile *dest,
- GVariant *variant,
- GCancellable *cancellable,
- GError **error);
-
-gboolean ot_util_variant_map (GFile *src,
- const GVariantType *type,
- GVariant **out_variant,
- GError **error);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UTIL_H__
-
-#include <ot-gio-utils.h>
-#include <ot-glib-compat.h>
-#include <ot-opt-utils.h>
-#include <ot-unix-utils.h>
-#include <ot-variant-utils.h>
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "ob-builtins.h"
-
-static OsbuildBuiltin builtins[] = {
- { "buildone", osbuild_builtin_buildone, 0 },
- { NULL }
-};
-
-static int
-usage (char **argv, gboolean is_error)
-{
- OsbuildBuiltin *builtin = builtins;
- void (*print_func) (const gchar *format, ...);
-
- if (is_error)
- print_func = g_printerr;
- else
- print_func = g_print;
-
- print_func ("usage: %s COMMAND [options]\n",
- argv[0]);
- print_func ("Builtin commands:\n");
-
- while (builtin->name)
- {
- print_func (" %s\n", builtin->name);
- builtin++;
- }
- return (is_error ? 1 : 0);
-}
-
-
-int
-main (int argc,
- char **argv)
-{
- OsbuildBuiltin *builtin;
- const char *cmd;
-
- g_type_init ();
-
- g_set_prgname (argv[0]);
-
- builtin = builtins;
-
- if (argc < 2)
- return usage (argv, 1);
-
- cmd = argv[1];
-
- while (builtin->name)
- {
- GError *error = NULL;
- if (strcmp (cmd, builtin->name) == 0)
- {
- int i;
- int tmp_argc;
- char **tmp_argv;
-
- tmp_argc = argc - 1;
- tmp_argv = g_new0 (char *, tmp_argc + 1);
-
- tmp_argv[0] = (char*)builtin->name;
- for (i = 0; i < tmp_argc; i++)
- tmp_argv[i+1] = argv[i+2];
- if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
- {
- g_free (tmp_argv);
- g_printerr ("%s\n", error->message);
- g_clear_error (&error);
- return 1;
- }
- g_free (tmp_argv);
- return 0;
- }
- builtin++;
- }
-
- g_printerr ("Unknown command '%s'\n", cmd);
- return usage (argv, 1);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "otutil.h"
-#include "ob-builtins.h"
-
-#include <glib/gi18n.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-static char *repo_path;
-static char *ref;
-static char *name;
-static char *generator;
-static char *resultdir;
-static gboolean raw;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { "rev", 'r', 0, G_OPTION_ARG_STRING, &ref, "Build using this tree", "rev" },
- { "name", 0, 0, G_OPTION_ARG_STRING, &name, "Name of the source", "source" },
- { "generator", 0, 0, G_OPTION_ARG_FILENAME, &generator, "Script to run on installed tree", "script" },
- { "raw", 0, 0, G_OPTION_ARG_NONE, &raw, "Do not instantiate a tree, use current", NULL },
- { "resultdir", 0, 0, G_OPTION_ARG_FILENAME, &resultdir, "Directory for output artifacts", "dir" },
- { NULL }
-};
-
-static char *
-get_tmpdir (void) G_GNUC_UNUSED;
-
-static char *
-get_tmpdir (void)
-{
- char *tmp_prefix = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
- char *ret;
-
- if (tmp_prefix)
- {
- ret = g_strdup_printf ("%s/osbuild", tmp_prefix);
- }
- else
- {
- tmp_prefix = g_strdup_printf ("/tmp/osbuild-%d", getuid ());
- if (!g_file_test (tmp_prefix, G_FILE_TEST_IS_DIR))
- {
- if (!mkdir (tmp_prefix, 0755))
- {
- g_printerr ("Failed to make logging directory %s\n", tmp_prefix);
- exit (1);
- }
- }
- ret = tmp_prefix;
- tmp_prefix = NULL;
- }
- g_free (tmp_prefix);
- return ret;
-}
-
-static gboolean
-open_log (const char *name,
- GOutputStream **out_log,
- GError **error) G_GNUC_UNUSED;
-
-static gboolean
-open_log (const char *name,
- GOutputStream **out_log,
- GError **error)
-{
- gboolean ret = FALSE;
- char *tmpdir = NULL;
- char *path = NULL;
- GFile *logf = NULL;
- GFileOutputStream *ret_log = NULL;
-
- path = g_strdup_printf ("%s/%s.log", tmpdir, name);
- logf = ot_util_new_file_for_path (path);
-
- ret_log = g_file_replace (logf, NULL, FALSE, 0, NULL, error);
- if (!ret_log)
- goto out;
-
- ret = TRUE;
- *out_log = (GOutputStream*)ret_log;
- ret_log = NULL;
- out:
- g_free (path);
- g_free (tmpdir);
- g_clear_object (&logf);
- g_clear_object (&ret_log);
- return ret;
-}
-
-gboolean
-osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
-
- context = g_option_context_new ("- Build current directory");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (!raw && !repo_path)
- {
- ot_util_usage_error (context, "A result directory must be specified with --resultdir", error);
- goto out;
- }
-
- if (!generator)
- generator = g_build_filename (LIBEXECDIR, "ostree", "generators", "default", NULL);
-
-
-
- out:
- if (context)
- g_option_context_free (context);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSBUILD_BUILTINS__
-#define __OSBUILD_BUILTINS__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
- OSBUILD_BUILTIN_FLAG_NONE = 0,
-} OsbuildBuiltinFlags;
-
-typedef struct {
- const char *name;
- gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
- int flags; /* OsbuildBuiltinFlags */
-} OsbuildBuiltin;
-
-gboolean osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include "otutil.h"
-
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-static const char *
-find_first_file (GFileTest test, const char *name, ...) G_GNUC_NULL_TERMINATED;
-
-static const char *
-find_first_file (GFileTest test, const char *name, ...)
-{
- va_list args;
-
- va_start (args, name);
-
- do
- {
- if (g_file_test (name, test))
- break;
- name = va_arg (args, const char *);
- }
- while (name != NULL);
-
- va_end (args);
-
- return name;
-}
-
-static void
-split_configure_make_args (int argc,
- char **argv,
- GPtrArray **out_configure_args,
- GPtrArray **out_make_args,
- GPtrArray **out_makeinstall_args)
-{
- int i;
-
- *out_configure_args = g_ptr_array_new ();
- *out_make_args = g_ptr_array_new ();
- *out_makeinstall_args = g_ptr_array_new ();
-
- for (i = 1; i < argc; i++)
- {
- if (g_str_has_prefix (argv[i], "--"))
- g_ptr_array_add (*out_configure_args, argv[i]);
- else if (g_str_has_prefix (argv[i], "DESTDIR="))
- g_ptr_array_add (*out_makeinstall_args, argv[i]);
- else
- g_ptr_array_add (*out_make_args, argv[i]);
- }
-}
-
-static void
-spawn_sync_or_fatal (char **args, char **env, GSpawnFlags flags)
-{
- GError *error = NULL;
- int estatus;
- char **iter;
-
- g_print ("osbuild: running: ");
- for (iter = args; *iter; iter++)
- g_print ("%s ", *iter);
- g_print ("\n");
- if (g_spawn_sync (NULL, args, env, flags, NULL, NULL, NULL, NULL, &estatus, &error))
- {
- if (WIFEXITED (estatus) && WEXITSTATUS (estatus) == 0)
- {
- g_message ("Subprocess %s exited successfully\n", args[0]);
- }
- else
- {
- if (WIFEXITED (estatus))
- g_error ("Subprocess %s exited with code %d\n", args[0], WEXITSTATUS (estatus));
- else if (WIFSIGNALED (estatus))
- g_error ("Subprocess %s killed by signal %d\n", args[0], WTERMSIG (estatus));
- else
- g_error ("Subprocess %s terminated with status %d\n", args[0], estatus);
- exit (1);
- }
- }
- else
- {
- g_error ("Failed to execute %s: %s\n", args[0], error->message);
- exit (1);
- }
-}
-
-static void
-ptr_array_extend (GPtrArray *dest, GPtrArray *to_append)
-{
- int i;
-
- for (i = 0; i < to_append->len; i++)
- g_ptr_array_add (dest, to_append->pdata[i]);
-}
-
-int
-main (int argc,
- char **argv)
-{
- GPtrArray *config_args;
- GPtrArray *make_args;
- GPtrArray *makeinstall_args;
- GPtrArray *args;
- char **subprocess_env;
-
- g_type_init ();
-
- g_set_prgname (argv[0]);
-
- args = g_ptr_array_new ();
-
- subprocess_env = g_get_environ ();
- ot_g_environ_setenv (subprocess_env, "LANG", "C", TRUE);
- ot_g_environ_unsetenv (subprocess_env, "LC_ALL");
-
- split_configure_make_args (argc, argv, &config_args, &make_args, &makeinstall_args);
-
- if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
- {
- const char *autogen;
- char **autogen_env;
-
- autogen = find_first_file (G_FILE_TEST_IS_EXECUTABLE, "./autogen", "./autogen.sh", NULL);
- if (!autogen)
- ot_util_fatal_literal ("No executable configure or autogen script found");
-
- autogen_env = g_strdupv (subprocess_env);
- ot_g_environ_setenv (autogen_env, "NOCONFIGURE", "1", TRUE);
-
- g_ptr_array_set_size (args, 0);
- g_ptr_array_add (args, (char*) autogen);
- g_ptr_array_add (args, NULL);
- spawn_sync_or_fatal ((char**)args->pdata, autogen_env, 0);
- }
-
- if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
- ot_util_fatal_literal ("autogen script failed to generate a configure script");
-
- g_ptr_array_set_size (args, 0);
- g_ptr_array_add (args, "./configure");
- ptr_array_extend (args, config_args);
- g_ptr_array_add (args, NULL);
- spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, 0);
-
- g_ptr_array_set_size (args, 0);
- g_ptr_array_add (args, "make");
- ptr_array_extend (args, make_args);
- g_ptr_array_add (args, NULL);
- spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
-
- g_ptr_array_set_size (args, 0);
- g_ptr_array_add (args, "make");
- g_ptr_array_add (args, "install");
- ptr_array_extend (args, makeinstall_args);
- g_ptr_array_add (args, NULL);
- spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
-
- return 0;
-}
+++ /dev/null
-#!/usr/bin/python
-#
-# ostree-buildone:
-# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
-# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
-
-# The build output is automatically logged to $TMPDIR/build-$(PWD).log.
-# For example, invoking metabuild in a directory named "foo" will log
-# to /tmp/build-foo.log
-#
-# You can pass arguments to metabuild; if they start with '--', they're
-# given to configure. Otherwise, they're passed to make.
-#
-# $ metabuild --enable-libfoo # passed to configure
-# $ metabuild -j 1 # passed to make
-
-import os,sys,subprocess,tempfile,re
-import select,time,stat,fcntl
-
-subprocess_nice_args = []
-
-# In the future we should test for this better; possibly implement a
-# custom fork handler
-try:
- chrt_args = ['chrt', '--idle', '0']
- proc = subprocess.Popen(chrt_args + ['true'])
- if proc.wait() == 0:
- subprocess_nice_args.extend(chrt_args)
-except OSError, e:
- pass
-
-try:
- ionice_args = ['ionice', '-c', '3', '-t']
- proc = subprocess.Popen(ionice_args + ['true'])
- if proc.wait() == 0:
- subprocess_nice_args.extend(ionice_args)
-except OSError, e:
- pass
-
-warning_re = re.compile(r'(: ((warning)|(error)|(fatal error)): )|(make(\[[0-9]+\])?: \*\*\*)')
-output_whitelist_re = re.compile(r'^(make(\[[0-9]+\])?: Entering directory)|(ostree-build )')
-
-_bold_sequence = None
-_normal_sequence = None
-if os.isatty(1):
- _bold_sequence = subprocess.Popen(['tput', 'bold'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
- _normal_sequence = subprocess.Popen(['tput', 'sgr0'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
-def _bold(text):
- if _bold_sequence is not None:
- return '%s%s%s' % (_bold_sequence, text, _normal_sequence)
- else:
- return text
-
-class Mainloop(object):
- DEFAULT = None
- def __init__(self):
- self._running = True
- self.poll = select.poll()
- self._timeouts = []
- self._pid_watches = {}
- self._fd_callbacks = {}
-
- @classmethod
- def get(cls, context):
- if context is None:
- if cls.DEFAULT is None:
- cls.DEFAULT = cls()
- return cls.DEFAULT
- raise NotImplementedError("Unknown context %r" % (context, ))
-
- def watch_fd(self, fd, callback):
- self.poll.register(fd)
- self._fd_callbacks[fd] = callback
-
- def unwatch_fd(self, fd):
- self.poll.unregister(fd)
- del self._fd_callbacks[fd]
-
- def watch_pid(self, pid, callback):
- self._pid_watches[pid] = callback
-
- def timeout_add(self, ms, callback):
- self._timeouts.append((ms, callback))
-
- def quit(self):
- self._running = False
-
- def run_once(self):
- min_timeout = None
- if len(self._pid_watches) > 0:
- min_timeout = 500
- for (ms, callback) in self._timeouts:
- if (min_timeout is None) or (ms < min_timeout):
- min_timeout = ms
- origtime = time.time() * 1000
- fds = self.poll.poll(min_timeout)
- for fd in fds:
- self._fd_callbacks[fd]()
- for pid in self._pid_watches:
- (opid, status) = os.waitpid(pid, os.WNOHANG)
- if opid != 0:
- self._pid_watches[pid](opid, status)
- del self._pid_watches[pid]
- newtime = time.time() * 1000
- diff = int(newtime - origtime)
- if diff < 0: diff = 0
- for i,(ms, callback) in enumerate(self._timeouts):
- remaining_ms = ms - diff
- if remaining_ms <= 0:
- callback()
- else:
- self._timeouts[i] = (remaining_ms, callback)
-
- def run(self):
- while self._running:
- self.run_once()
-
-class FileMonitor(object):
- def __init__(self):
- self._paths = {}
- self._path_modtimes = {}
- self._timeout = 1000
- self._timeout_installed = False
- self._loop = Mainloop.get(None)
-
- def _stat(self, path):
- try:
- st = os.stat(path)
- return st[stat.ST_MTIME]
- except OSError, e:
- return None
-
- def add(self, path, callback):
- if path not in self._paths:
- self._paths[path] = []
- self._path_modtimes[path] = self._stat(path)
- self._paths[path].append(callback)
- if not self._timeout_installed:
- self._timeout_installed = True
- self._loop.timeout_add(self._timeout, self._check_files)
-
- def _check_files(self):
- for (path,callbacks) in self._paths.iteritems():
- mtime = self._stat(path)
- orig_mtime = self._path_modtimes[path]
- if (mtime is not None) and (orig_mtime is None or (mtime > orig_mtime)):
- self._path_modtimes[path] = mtime
- for cb in callbacks:
- cb()
-
-_filemon = FileMonitor()
-
-class OutputFilter(object):
- def __init__(self, filename, output):
- self.filename = filename
- self.output = output
-
- # inherit globals
- self._warning_re = warning_re
- self._nonfilter_re = output_whitelist_re
-
- self._buf = ''
- self._warning_count = 0
- self._filtered_line_count = 0
- _filemon.add(filename, self._on_changed)
- self._fd = os.open(filename, os.O_RDONLY)
- fcntl.fcntl(self._fd, fcntl.F_SETFL, os.O_NONBLOCK)
-
- def _do_read(self):
- while True:
- buf = os.read(self._fd, 4096)
- if buf == '':
- break
- self._buf += buf
- self._flush()
-
- def _write_last_log_lines(self):
- _last_line_limit = 100
- f = open(logfile_path)
- lines = []
- for line in f:
- if line.startswith('ostree-build '):
- continue
- lines.append(line)
- if len(lines) > _last_line_limit:
- lines.pop(0)
- f.close()
- for line in lines:
- self.output.write('| ')
- self.output.write(line)
-
- def _flush(self):
- while True:
- p = self._buf.find('\n')
- if p < 0:
- break
- line = self._buf[0:p]
- self._buf = self._buf[p+1:]
- match = self._warning_re.search(line)
- if match:
- self._warning_count += 1
- self.output.write(line + '\n')
- else:
- match = self._nonfilter_re.search(line)
- if match:
- self.output.write(line + '\n')
- else:
- self._filtered_line_count += 1
-
- def _on_changed(self):
- self._do_read()
-
- def start(self):
- self._do_read()
-
- def finish(self, successful):
- self._do_read()
- if not successful:
- self._write_last_log_lines()
- pass
- self.output.write("ostree-build %s: %d warnings\n" % ('success' if successful else _bold('failed'),
- self._warning_count, ))
- self.output.write("ostree-build: full log path: %s\n" % (logfile_path, ))
-
- if successful:
- for f in os.listdir('_build'):
- path = os.path.join('_build', f)
- if f.startswith('artifact-'):
- self.output.write("ostree-build: created artifact: %s\n" % (f, ))
- sys.exit(0 if successful else 1)
-
-def _on_makeinstall_exit(pid, estatus):
- _output_filter.finish(estatus == 0)
-
-def _on_make_exit(pid, estatus):
- if estatus == 0:
- args = list(subprocess_nice_args)
- args.append('ostree-buildone-makeinstall-split-artifacts')
- _logfile_f.write("Running: %r\n" % (args, ))
- _logfile_f.flush()
- proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
- _loop.watch_pid(proc.pid, _on_makeinstall_exit)
- else:
- _output_filter.finish(False)
-
-def _get_version():
- if not os.path.isdir('.git'):
- sys.stderr.write("ostree-buildone: error: Couldn't find .git directory")
- sys.exit(1)
-
- proc = subprocess.Popen(['git', 'describe'], stdout=subprocess.PIPE)
- output = proc.communicate()[0].strip()
- if proc.wait() != 0:
- proc = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
- if proc.wait() != 0:
- sys.stderr.write("ostree-buildone: error: git rev-parse HEAD failed")
- sys.exit(1)
- output = proc.communicate()[0].strip()
- return output
-
-if __name__ == '__main__':
- user_tmpdir = os.environ.get('XDG_RUNTIME_DIR')
- if user_tmpdir is None:
- user_tmpdir = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'metabuild-%s' % (os.getuid(), ))
- else:
- user_tmpdir = os.path.join(user_tmpdir, 'ostree-build')
-
- os.environ['OSBUILD_VERSION'] = _get_version()
-
- if os.path.isdir('_build'):
- for filename in os.listdir('_build'):
- path = os.path.join('_build', filename)
- if filename.startswith('artifact-'):
- os.unlink(path)
-
- if not os.path.isdir(user_tmpdir):
- os.makedirs(user_tmpdir)
- logfile_path = os.path.join(user_tmpdir, '%s.log' % (os.path.basename(os.getcwd()), ))
- try:
- os.unlink(logfile_path)
- except OSError, e:
- pass
- logfile_write_fd = os.open(logfile_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
- global _logfile_f
- _logfile_f = os.fdopen(logfile_write_fd, "w")
- sys.stdout.write('ostree-build: logging to %r\n' % (logfile_path, ))
- sys.stdout.flush()
-
- global _output_filter
- _output_filter = OutputFilter(logfile_path, sys.stdout)
- _output_filter.start()
-
- args = list(subprocess_nice_args)
- args.append('ostree-buildone-make')
- args.extend(sys.argv[1:])
- devnull=open('/dev/null')
- _logfile_f.write("Running: %r\n" % (args, ))
- _logfile_f.flush()
- proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
-
- global _loop
- _loop = Mainloop.get(None)
- _loop.watch_pid(proc.pid, _on_make_exit)
- _loop.run()
+++ /dev/null
-#!/usr/bin/python
-
-# ostree-buildone-raw: Generic build system wrapper
-# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
-# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
-
-# ostree-buildone-raw wraps systems that implement the GNOME build API:
-# http://people.gnome.org/~walters/docs/build-api.txt
-
-import os,sys,subprocess,tempfile,re
-from multiprocessing import cpu_count
-import select,time
-
-root = None
-
-prefix = '/usr'
-
-# libdir detection
-if os.path.isdir('/lib64'):
- libdir=os.path.join(prefix, 'lib64')
-else:
- libdir=os.path.join(prefix, 'lib')
-
-default_buildapi_jobs = ['-j', '%d' % (cpu_count() * 2, )]
-configargs = ['--prefix=' + prefix,
- '--libdir=' + libdir,
- '--sysconfdir=/etc',
- '--localstatedir=/var',
- '--bindir=' + os.path.join(prefix, 'bin'),
- '--sbindir=' + os.path.join(prefix, 'sbin'),
- '--datadir=' + os.path.join(prefix, 'share'),
- '--includedir=' + os.path.join(prefix, 'include'),
- '--libexecdir=' + os.path.join(prefix, 'libexec'),
- '--mandir=' + os.path.join(prefix, 'share', 'man'),
- '--infodir=' + os.path.join(prefix, 'share', 'info')]
-makeargs = ['make']
-
-top_srcdir=os.getcwd()
-
-for arg in sys.argv[1:]:
- if arg.startswith('--'):
- configargs.append(arg)
- else:
- makeargs.append(arg)
-
-def log(msg):
- fullmsg = 'ostree-buildone: ' + msg + '\n'
- sys.stdout.write(fullmsg)
- sys.stdout.flush()
-
-def fatal(msg):
- log(msg)
- sys.exit(1)
-
-def run_sync(args, env=None):
- log("Running: %r" % (args, ))
- f = open('/dev/null', 'r')
- proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
- close_fds=True, env=env)
- f.close()
- returncode = proc.wait()
- log("pid %d exited with code %d" % (proc.pid, returncode))
- if returncode != 0:
- sys.exit(1)
-
-class BuildSystemScanner(object):
- @classmethod
- def _find_file(cls, names):
- for name in names:
- if os.path.exists(name):
- return name
- return None
-
- @classmethod
- def get_configure_source_script(cls):
- return cls._find_file(('./configure.ac', './configure.in'))
-
- @classmethod
- def get_configure_script(cls):
- return cls._find_file(('./configure', ))
-
- @classmethod
- def get_bootstrap_script(cls):
- return cls._find_file(('./autogen.sh', ))
-
- @classmethod
- def get_silent_rules(cls):
- src = cls.get_configure_source_script()
- if not src:
- return False
- f = open(src)
- for line in f:
- if line.find('AM_SILENT_RULES') >= 0:
- f.close()
- return True
- f.close()
- return False
-
-def _search_file(filename, pattern):
- f = open(filename)
- for line in f:
- if line.startswith(pattern):
- f.close()
- return line
- f.close()
- return None
-
-def _find_buildapi_makevariable(name):
- var = '.%s:' % (name, )
- line = None
- if os.path.exists('Makefile.in'):
- line = _search_file('Makefile.in', var)
- if not line and os.path.exists('Makefile'):
- line = _search_file('Makefile', var)
- return line is not None
-
-def phase_bootstrap():
- have_configure = BuildSystemScanner.get_configure_script()
- have_configure_source = BuildSystemScanner.get_configure_source_script()
- if not (have_configure or have_configure_source):
- fatal("No configure or bootstrap script detected; unknown buildsystem")
- return
-
- need_v1 = BuildSystemScanner.get_silent_rules()
- if need_v1:
- log("Detected AM_SILENT_RULES, adding --disable-silent-rules to configure")
- configargs.append('--disable-silent-rules')
-
- if have_configure:
- phase_configure()
- else:
- bootstrap = BuildSystemScanner.get_bootstrap_script()
- if bootstrap:
- log("Detected bootstrap script: %s, using it" % (bootstrap, ))
- args = [bootstrap]
- args.extend(configargs)
- # Add NOCONFIGURE; GNOME style scripts use this
- env = dict(os.environ)
- env['NOCONFIGURE'] = '1'
- run_sync(args, env=env)
- else:
- log("No bootstrap script found; using generic autoreconf")
- run_sync(['autoreconf', '-f', '-i'])
- phase_configure()
-
-def phase_configure():
- use_builddir = True
- doesnot_support_builddir = _find_buildapi_makevariable('buildapi-no-builddir')
- if doesnot_support_builddir:
- log("Found .buildapi-no-builddir; copying source tree to _build")
- shutil.rmtree('_build')
- os.mkdir('_build')
- shutil.copytree('.', '_build', symlinks=True,
- ignore=shutil.ignore_patterns('_build'))
- use_builddir = False
- builddir = '.'
- else:
- builddir = '_build'
-
- if not use_builddir:
- configdir = './'
- else:
- configdir = os.getcwd()
- builddir = builddir
- log("Using build directory %r" % (builddir, ))
- if not os.path.isdir(builddir):
- os.mkdir(builddir)
- os.chdir(builddir)
-
- configstatus = 'config.status'
- if not os.path.exists(configstatus):
- args = [os.path.join(configdir, 'configure')]
- args.extend(configargs)
- run_sync(args)
- else:
- log("Found %s, skipping configure" % (configstatus, ))
- phase_build()
-
-build_status = False
-
-def phase_build():
- if not os.path.exists('Makefile'):
- log("No Makefile found")
- sys.exit(1)
- args = makeargs
- user_specified_jobs = False
- for arg in args:
- if arg == '-j':
- user_specified_jobs = True
-
- if not user_specified_jobs:
- notparallel = _find_buildapi_makevariable('NOTPARALLEL')
- if not notparallel:
- log("Didn't find NOTPARALLEL, using parallel make by default")
- args.extend(default_buildapi_jobs)
-
- run_sync(args)
-
-def phase_complete():
- sys.exit(0)
-
-log("invocation arguments: %r" % (sys.argv, ))
-
-# Start off the process
-phase_bootstrap()
+++ /dev/null
-#!/usr/bin/python
-
-# ostree-buildone-raw: Generic build system wrapper
-# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
-# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
-
-import os,sys,re,subprocess
-import tempfile,shutil
-
-_devel_regexps = map(re.compile,
- [r'/usr/include/',
- r'/usr/share/pkgconfig/',
- r'/.*lib(?:|(?:32)|(?:64))/pkgconfig/.*\.pc',
- r'/.*lib(?:|(?:32)|(?:64))/.*\.so$'])
-
-def log(msg):
- fullmsg = 'ostree-buildone: ' + msg + '\n'
- sys.stdout.write(fullmsg)
- sys.stdout.flush()
-
-tempfiles = []
-
-def do_exit(code):
- for tmpname in tempfiles:
- if os.path.isdir(tmpname):
- shutil.rmtree(tmpname)
- else:
- try:
- os.unlink(tmpname)
- pass
- except OSError, e:
- pass
- sys.exit(code)
-
-def fatal(msg):
- log(msg)
- do_exit(1)
-
-def run_sync(args, env=None):
- log("Running: %r" % (args, ))
- f = open('/dev/null', 'r')
- proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
- close_fds=True, env=env)
- f.close()
- returncode = proc.wait()
- if returncode == 0:
- func = log
- else:
- func = fatal
- func("pid %d exited with code %d" % (proc.pid, returncode))
-
-basename=os.path.basename(os.getcwd())
-artifact_prefix='artifact-%s,%s' % (basename, os.environ['OSBUILD_VERSION'])
-origdir=os.getcwd()
-os.chdir('_build')
-
-if not os.path.exists('Makefile'):
- log("No Makefile found")
- do_exit(1)
-
-(fd,fakeroot_temp)=tempfile.mkstemp(prefix='ostree-fakeroot-%s-' % (basename,))
-os.close(fd)
-tempfiles.append(fakeroot_temp)
-tempdir = tempfile.mkdtemp(prefix='ostree-build-%s-' % (basename,))
-tempfiles.append(tempdir)
-args = ['fakeroot', '-s', fakeroot_temp, 'make', 'install', 'DESTDIR=' + tempdir]
-run_sync(args)
-
-devel_files = set()
-runtime_files = set()
-
-oldpwd=os.getcwd()
-os.chdir(tempdir)
-for root, dirs, files in os.walk('.'):
- for filename in files:
- path = os.path.join(root, filename)
- matched = False
- for r in _devel_regexps:
- if r.match(path[1:]):
- devel_files.add(path)
- matched = True
- break
- if not matched:
- runtime_files.add(path)
-os.chdir(oldpwd)
-
-def make_artifact(name, from_files):
- artifact_target = '%s-%s.tar.gz' % (artifact_prefix, name)
- (fd,filelist_temp)=tempfile.mkstemp(prefix='ostree-filelist-%s-%s' % (basename, name))
- os.close(fd)
- tempfiles.append(filelist_temp)
- f = open(filelist_temp, 'w')
- for filename in from_files:
- f.write(filename)
- f.write('\n')
- f.close()
- args = ['fakeroot', '-i', fakeroot_temp, 'tar', '-c', '-z', '-C', tempdir, '-f', artifact_target, '-T', filelist_temp]
- run_sync(args)
-
-if devel_files:
- make_artifact('devel', devel_files)
-make_artifact('runtime', runtime_files)
-
-do_exit(0)
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "ot-builtins.h"
-
-static OstreeBuiltin builtins[] = {
- { "checkout", ostree_builtin_checkout, 0 },
- { "diff", ostree_builtin_diff, 0 },
- { "init", ostree_builtin_init, 0 },
- { "commit", ostree_builtin_commit, 0 },
- { "compose", ostree_builtin_compose, 0 },
- { "log", ostree_builtin_log, 0 },
-#ifdef HAVE_LIBSOUP_GNOME
- { "pull", ostree_builtin_pull, 0 },
-#endif
- { "fsck", ostree_builtin_fsck, 0 },
- { "remote", ostree_builtin_remote, 0 },
- { "rev-parse", ostree_builtin_rev_parse, 0 },
- { "remote", ostree_builtin_remote, 0 },
- { "run-triggers", ostree_builtin_run_triggers, 0 },
- { "show", ostree_builtin_show, 0 },
- { NULL }
-};
-
-static int
-usage (char **argv, gboolean is_error)
-{
- OstreeBuiltin *builtin = builtins;
- void (*print_func) (const gchar *format, ...);
-
- if (is_error)
- print_func = g_printerr;
- else
- print_func = g_print;
-
- print_func ("usage: %s --repo=PATH COMMAND [options]\n",
- argv[0]);
- print_func ("Builtin commands:\n");
-
- while (builtin->name)
- {
- print_func (" %s\n", builtin->name);
- builtin++;
- }
- return (is_error ? 1 : 0);
-}
-
-
-int
-main (int argc,
- char **argv)
-{
- OstreeBuiltin *builtin;
- const char *cmd;
- const char *repo;
-
- g_type_init ();
-
- g_set_prgname (argv[0]);
-
- builtin = builtins;
-
- if (argc < 3)
- return usage (argv, 1);
-
- if (!g_str_has_prefix (argv[1], "--repo="))
- return usage (argv, 1);
- repo = argv[1] + strlen ("--repo=");
-
- cmd = argv[2];
-
- while (builtin->name)
- {
- GError *error = NULL;
- if (strcmp (cmd, builtin->name) == 0)
- {
- int i;
- int tmp_argc;
- char **tmp_argv;
-
- tmp_argc = argc - 2;
- tmp_argv = g_new0 (char *, tmp_argc + 1);
-
- tmp_argv[0] = (char*)builtin->name;
- for (i = 0; i < tmp_argc; i++)
- tmp_argv[i+1] = argv[i+3];
- if (!builtin->fn (tmp_argc, tmp_argv, repo, &error))
- {
- g_free (tmp_argv);
- g_printerr ("%s\n", error->message);
- g_clear_error (&error);
- return 1;
- }
- g_free (tmp_argv);
- return 0;
- }
- builtin++;
- }
-
- g_printerr ("Unknown command '%s'\n", cmd);
- return usage (argv, 1);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
- { NULL }
-};
-
-gboolean
-ostree_builtin_checkout (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *commit;
- const char *destination;
- GFile *destf = NULL;
-
- context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 3)
- {
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "COMMIT and DESTINATION must be specified");
- goto out;
- }
-
- commit = argv[1];
- destination = argv[2];
-
- if (!ostree_repo_checkout (repo, commit, destination, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- g_clear_object (&destf);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <gio/gunixoutputstream.h>
-
-#include <glib/gi18n.h>
-
-static gboolean separator_null;
-static int from_fd = -1;
-static gboolean from_stdin;
-static char *from_file;
-static char *metadata_text_path;
-static char *metadata_bin_path;
-static char *subject;
-static char *body;
-static char *parent;
-static char *branch;
-
-static GOptionEntry options[] = {
- { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
- { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
- { "metadata-variant-text", 0, 0, G_OPTION_ARG_FILENAME, &metadata_text_path, "File containing g_variant_print() output", "path" },
- { "metadata-variant", 0, 0, G_OPTION_ARG_FILENAME, &metadata_bin_path, "File containing serialized variant, in host endianness", "path" },
- { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
- { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
- { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
- { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
- { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
- { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
- { NULL }
-};
-
-typedef struct {
- GFile *dir;
- char separator;
- GOutputStream *out;
- GCancellable *cancellable;
-} FindThreadData;
-
-static gboolean
-find (const char *basepath,
- GFile *dir,
- char separator,
- GOutputStream *out,
- GCancellable *cancellable,
- GError **error);
-
-static gboolean
-find_write_child (const char *basepath,
- GFile *dir,
- char separator,
- GOutputStream *out,
- GFileInfo *finfo,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- guint32 type;
- const char *name;
- char buf[1];
- char *child_path = NULL;
- GString *child_trimmed_path = NULL;
- GFile *child = NULL;
- gsize bytes_written;
-
- type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
- name = g_file_info_get_attribute_byte_string (finfo, "standard::name");
-
- child = g_file_get_child (dir, name);
-
- if (type == G_FILE_TYPE_DIRECTORY)
- {
- if (!find (basepath, child, separator, out, cancellable, error))
- goto out;
- }
-
- child_path = g_file_get_path (child);
- child_trimmed_path = g_string_new (child_path + strlen (basepath));
- if (!*(child_trimmed_path->str))
- {
- /* do nothing - we implicitly add the root . */
- }
- else
- {
- g_assert (*(child_trimmed_path->str) == '/');
- g_string_insert_c (child_trimmed_path, 0, '.');
-
- if (!g_output_stream_write_all (out, child_trimmed_path->str, child_trimmed_path->len,
- &bytes_written, cancellable, error))
- goto out;
- buf[0] = separator;
- if (!g_output_stream_write_all (out, buf, 1, &bytes_written,
- cancellable, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- g_string_free (child_trimmed_path, TRUE);
- child_trimmed_path = NULL;
- g_free (child_path);
- child_path = NULL;
- g_clear_object (&child);
- return ret;
-}
-
-static gboolean
-find (const char *basepath,
- GFile *dir,
- char separator,
- GOutputStream *out,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- GError *temp_error = NULL;
- GFileEnumerator *enumerator = NULL;
- GFileInfo *finfo = NULL;
-
- enumerator = g_file_enumerate_children (dir, "standard::type,standard::name",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable, error);
- if (!enumerator)
- goto out;
-
- while ((finfo = g_file_enumerator_next_file (enumerator, cancellable, error)) != NULL)
- {
- if (!find_write_child (basepath, dir, separator, out, finfo, cancellable, error))
- goto out;
- g_clear_object (&finfo);
- }
- if (temp_error)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
-
- ret = TRUE;
- out:
- g_clear_object (&finfo);
- g_clear_object (&enumerator);
- return ret;
-}
-
-static gpointer
-find_thread (gpointer data)
-{
- FindThreadData *tdata = data;
- GError *error = NULL;
- char *path;
-
- path = g_file_get_path (tdata->dir);
- if (!find (path, tdata->dir, tdata->separator, tdata->out,
- tdata->cancellable, &error))
- {
- g_printerr ("%s", error->message);
- g_clear_error (&error);
- }
- g_free (path);
- g_clear_object (&(tdata->dir));
- g_clear_object (&(tdata->out));
- return NULL;
-}
-
-gboolean
-ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- char *dir = NULL;
- GChecksum *commit_checksum = NULL;
- char separator;
- GVariant *metadata = NULL;
- GMappedFile *metadata_mappedf = NULL;
- GFile *metadata_f = NULL;
- gboolean temp_fd = -1;
- int pipefd[2] = { -1, -1 };
- GOutputStream *out = NULL;
- FindThreadData fdata;
-
- context = g_option_context_new ("[DIR] - Commit a new revision");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (argc > 1)
- dir = g_strdup (argv[1]);
- else
- dir = g_get_current_dir ();
-
- if (g_str_has_suffix (dir, "/"))
- dir[strlen (dir) - 1] = '\0';
-
- separator = separator_null ? '\0' : '\n';
-
- if (!*dir)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid empty directory");
- goto out;
- }
-
- if (metadata_text_path || metadata_bin_path)
- {
- metadata_mappedf = g_mapped_file_new (metadata_text_path ? metadata_text_path : metadata_bin_path, FALSE, error);
- if (!metadata_mappedf)
- goto out;
- if (metadata_text_path)
- {
- metadata = g_variant_parse (G_VARIANT_TYPE ("a{sv}"),
- g_mapped_file_get_contents (metadata_mappedf),
- g_mapped_file_get_contents (metadata_mappedf) + g_mapped_file_get_length (metadata_mappedf),
- NULL, error);
- if (!metadata)
- goto out;
- }
- else if (metadata_bin_path)
- {
- metadata_f = ot_util_new_file_for_path (metadata_bin_path);
- if (!ot_util_variant_map (metadata_f, G_VARIANT_TYPE ("a{sv}"), &metadata, error))
- goto out;
- }
- else
- g_assert_not_reached ();
- }
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (!branch)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A branch must be specified with --branch");
- goto out;
- }
-
- if (!subject)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A subject must be specified with --subject");
- goto out;
- }
-
- if (!(from_file || from_fd >= 0 || from_stdin))
- {
- /* We're using the current directory */
-
- }
-
- if (from_stdin)
- from_fd = 0;
- else if (from_file)
- {
- temp_fd = ot_util_open_file_read (from_file, error);
- if (temp_fd < 0)
- {
- g_prefix_error (error, "Failed to open '%s': ", from_file);
- goto out;
- }
- from_fd = temp_fd;
- }
- else
- {
- if (pipe (pipefd) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- out = (GOutputStream*)g_unix_output_stream_new (pipefd[1], TRUE);
- from_fd = pipefd[0];
-
- fdata.dir = ot_util_new_file_for_path (dir);
- fdata.separator = separator;
- fdata.out = out;
- fdata.cancellable = NULL;
-
- if (g_thread_create_full (find_thread, &fdata, 0, FALSE, FALSE, G_THREAD_PRIORITY_NORMAL, error) == NULL)
- goto out;
-
- out = NULL;
- }
-
- if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, metadata,
- dir, from_fd, separator,
- &commit_checksum, error))
- goto out;
-
- ret = TRUE;
- g_print ("%s\n", g_checksum_get_string (commit_checksum));
- out:
- g_clear_object (&out);
- if (temp_fd >= 0)
- (void)close (temp_fd);
- if (pipefd[0] > 0)
- (void) close (pipefd[0]);
- if (pipefd[1] > 0)
- (void) close (pipefd[1]);
- g_free (dir);
- if (metadata_mappedf)
- g_mapped_file_unref (metadata_mappedf);
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- if (commit_checksum)
- g_checksum_free (commit_checksum);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *compose_metadata_path;
-
-static GOptionEntry options[] = {
- { "out-metadata", 0, 0, G_OPTION_ARG_FILENAME, &compose_metadata_path, "Output a file containing serialized metadata about the compose, in host endianness", "path" },
- { NULL }
-};
-
-static void
-rm_rf (GFile *path)
-{
- GFileInfo *finfo = NULL;
- GFileEnumerator *path_enum = NULL;
- guint32 type;
-
- finfo = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL, NULL);
- if (!finfo)
- goto out;
-
- type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
- if (type == G_FILE_TYPE_DIRECTORY)
- {
- path_enum = g_file_enumerate_children (path, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL, NULL);
- if (!path_enum)
- goto out;
-
-
- g_clear_object (&finfo);
- while ((finfo = g_file_enumerator_next_file (path_enum, NULL, NULL)) != NULL)
- {
- GFile *child = g_file_get_child (path, g_file_info_get_attribute_byte_string (finfo, "standard::name"));
- rm_rf (child);
- g_clear_object (&child);
- g_clear_object (&finfo);
- }
- }
-
- (void) g_file_delete (path, NULL, NULL);
-
- out:
- g_clear_object (&finfo);
- g_clear_object (&path_enum);
-}
-
-static gboolean
-merge_dir (GFile *destination,
- GFile *src,
- GError **error)
-{
- gboolean ret = FALSE;
- char *dest_path = NULL;
- char *src_path = NULL;
- GError *temp_error = NULL;
- GFileInfo *src_fileinfo = NULL;
- GFileInfo *dest_fileinfo = NULL;
- GFileEnumerator *src_enum = NULL;
- GFile *dest_subfile = NULL;
- GFile *src_subfile = NULL;
- const char *name;
- guint32 type;
-
- dest_path = g_file_get_path (destination);
- src_path = g_file_get_path (src);
-
- dest_fileinfo = g_file_query_info (destination, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL, &temp_error);
- if (dest_fileinfo)
- {
- type = g_file_info_get_attribute_uint32 (dest_fileinfo, "standard::type");
- if (type != G_FILE_TYPE_DIRECTORY)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Attempting to replace non-directory %s with directory %s",
- dest_path, src_path);
- goto out;
- }
-
- src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL, error);
- if (!src_enum)
- goto out;
-
- while ((src_fileinfo = g_file_enumerator_next_file (src_enum, NULL, &temp_error)) != NULL)
- {
- type = g_file_info_get_attribute_uint32 (src_fileinfo, "standard::type");
- name = g_file_info_get_attribute_byte_string (src_fileinfo, "standard::name");
-
- dest_subfile = g_file_get_child (destination, name);
- src_subfile = g_file_get_child (src, name);
-
- if (type == G_FILE_TYPE_DIRECTORY)
- {
- if (!merge_dir (dest_subfile, src_subfile, error))
- goto out;
- }
- else
- {
- if (!g_file_delete (dest_subfile, NULL, &temp_error))
- {
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- g_clear_error (&temp_error);
- else
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- }
- if (!g_file_move (src_subfile, dest_subfile, 0, NULL, NULL, NULL, error))
- goto out;
- }
-
- g_clear_object (&dest_subfile);
- g_clear_object (&src_subfile);
- }
- if (temp_error)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- }
- else if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&temp_error);
- if (!g_file_move (src, destination, 0, NULL, NULL, NULL, error))
- goto out;
- }
- else
- goto out;
-
- ret = TRUE;
- out:
- g_free (dest_path);
- g_free (src_path);
- g_clear_object (&src_fileinfo);
- g_clear_object (&dest_fileinfo);
- g_clear_object (&src_enum);
- g_clear_object (&dest_subfile);
- g_clear_object (&src_subfile);
- return ret;
-}
-
-static gboolean
-compose_branch_on_dir (OstreeRepo *repo,
- GFile *destination,
- const char *branch,
- GVariantBuilder *metadata_builder,
- GError **error)
-{
- char *destpath = NULL;
- char *branchpath = NULL;
- GFile *branchf = NULL;
- GFileEnumerator *enumerator = NULL;
- gboolean ret = FALSE;
- char *branchrev = NULL;
-
- if (!ostree_repo_resolve_rev (repo, branch, &branchrev, error))
- goto out;
-
- destpath = g_file_get_path (destination);
- if (g_str_has_suffix (destpath, "/"))
- destpath[strlen (destpath) - 1] = '\0';
- branchpath = g_strconcat (destpath, "-tmp-checkout-", branchrev, NULL);
- branchf = ot_util_new_file_for_path (branchpath);
-
- g_print ("Checking out %s (commit %s)...\n", branch, branchrev);
- if (!ostree_repo_checkout (repo, branchrev, branchpath, NULL, error))
- goto out;
- g_print ("...done\n");
- g_print ("Merging over destination...\n");
- if (!merge_dir (destination, branchf, error))
- goto out;
-
- if (metadata_builder)
- g_variant_builder_add (metadata_builder, "(ss)", branch, branchrev);
-
- ret = TRUE;
- out:
- if (branchf)
- rm_rf (branchf);
- g_clear_object (&enumerator);
- g_clear_object (&branchf);
- g_free (branchrev);
- g_free (destpath);
- g_free (branchpath);
- return ret;
-}
-
-gboolean
-ostree_builtin_compose (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *destination;
- GFile *destf = NULL;
- gboolean compose_metadata_builder_initialized = FALSE;
- GVariantBuilder compose_metadata_builder;
- gboolean commit_metadata_builder_initialized = FALSE;
- GVariantBuilder commit_metadata_builder;
- GVariant *commit_metadata = NULL;
- GFile *metadata_f = NULL;
- int i;
-
- context = g_option_context_new ("DESTINATION BRANCH1 BRANCH2 ... - Merge multiple commits into a single filesystem tree");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 3)
- {
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "DESTINATION and at least one COMMIT must be specified");
- goto out;
- }
-
- destination = argv[1];
- destf = ot_util_new_file_for_path (destination);
-
- if (compose_metadata_path)
- {
- compose_metadata_builder_initialized = TRUE;
- g_variant_builder_init (&compose_metadata_builder, G_VARIANT_TYPE ("a(ss)"));
- }
-
- for (i = 2; i < argc; i++)
- {
- const char *branch = argv[i];
-
- if (!compose_branch_on_dir (repo, destf, branch, compose_metadata_builder_initialized ? &compose_metadata_builder : NULL, error))
- goto out;
- }
-
- if (compose_metadata_path)
- {
- commit_metadata_builder_initialized = TRUE;
- g_variant_builder_init (&commit_metadata_builder, G_VARIANT_TYPE ("a{sv}"));
-
- g_variant_builder_add (&commit_metadata_builder, "{sv}",
- "ostree-compose", g_variant_builder_end (&compose_metadata_builder));
- compose_metadata_builder_initialized = FALSE;
-
- metadata_f = ot_util_new_file_for_path (compose_metadata_path);
-
- commit_metadata = g_variant_builder_end (&commit_metadata_builder);
- if (!ot_util_variant_save (metadata_f, commit_metadata, NULL, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- if (compose_metadata_builder_initialized)
- g_variant_builder_clear (&compose_metadata_builder);
- if (commit_metadata_builder_initialized)
- g_variant_builder_clear (&commit_metadata_builder);
- if (context)
- g_option_context_free (context);
- if (commit_metadata)
- g_variant_unref (commit_metadata);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- g_clear_object (&destf);
- g_clear_object (&metadata_f);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
- { NULL }
-};
-
-gboolean
-ostree_builtin_diff (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *target;
- const char *rev;
- GFile *targetf = NULL;
- GPtrArray *modified = NULL;
- GPtrArray *removed = NULL;
- GPtrArray *added = NULL;
-
- context = g_option_context_new ("REV TARGETDIR - Compare directory TARGETDIR against revision REV");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 3)
- {
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "REV and TARGETDIR must be specified");
- goto out;
- }
-
- rev = argv[1];
- target = argv[2];
- targetf = ot_util_new_file_for_path (target);
-
- if (!ostree_repo_diff (repo, rev, targetf, &modified, &removed, &added, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- g_clear_object (&repo);
- g_clear_object (&targetf);
- if (modified)
- g_ptr_array_free (modified, TRUE);
- if (removed)
- g_ptr_array_free (removed, TRUE);
- if (added)
- g_ptr_array_free (added, TRUE);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean quiet;
-
-static GOptionEntry options[] = {
- { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
- { NULL }
-};
-
-typedef struct {
- guint n_objects;
- gboolean had_error;
-} OtFsckData;
-
-static gboolean
-checksum_packed_file (OtFsckData *data,
- const char *path,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- GChecksum *ret_checksum = NULL;
- GFile *file = NULL;
- char *metadata_buf = NULL;
- GVariant *metadata = NULL;
- GVariant *xattrs = NULL;
- GFileInputStream *in = NULL;
- guint32 metadata_len;
- guint32 version, uid, gid, mode;
- guint64 content_len;
- gsize bytes_read;
- char buf[8192];
-
- file = ot_util_new_file_for_path (path);
-
- in = g_file_read (file, NULL, error);
- if (!in)
- goto out;
-
- if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
- goto out;
-
- metadata_len = GUINT32_FROM_BE (metadata_len);
-
- metadata_buf = g_malloc (metadata_len);
-
- if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
- goto out;
-
- metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
- metadata_buf, metadata_len, FALSE, NULL, NULL);
-
- g_variant_get (metadata, "(uuuu@a(ayay)t)",
- &version, &uid, &gid, &mode,
- &xattrs, &content_len);
- uid = GUINT32_FROM_BE (uid);
- gid = GUINT32_FROM_BE (gid);
- mode = GUINT32_FROM_BE (mode);
- content_len = GUINT64_FROM_BE (content_len);
-
- ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
- do
- {
- if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
- goto out;
- g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
- }
- while (bytes_read > 0);
-
- ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
- g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
-
- ret = TRUE;
- *out_checksum = ret_checksum;
- ret_checksum = NULL;
- out:
- if (ret_checksum)
- g_checksum_free (ret_checksum);
- g_free (metadata_buf);
- g_clear_object (&file);
- g_clear_object (&in);
- if (metadata)
- g_variant_unref (metadata);
- if (xattrs)
- g_variant_unref (xattrs);
- return ret;
-}
-
-static void
-object_iter_callback (OstreeRepo *repo,
- const char *path,
- GFileInfo *file_info,
- gpointer user_data)
-{
- OtFsckData *data = user_data;
- struct stat stbuf;
- GChecksum *checksum = NULL;
- GError *error = NULL;
- char *dirname = NULL;
- char *checksum_prefix = NULL;
- char *checksum_string = NULL;
- char *filename_checksum = NULL;
- gboolean packed = FALSE;
- OstreeObjectType objtype;
- char *dot;
-
- /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
- if (nlinks < 2 && !quiet)
- g_printerr ("note: floating object: %s\n", path); */
-
- if (g_str_has_suffix (path, ".meta"))
- objtype = OSTREE_OBJECT_TYPE_META;
- else if (g_str_has_suffix (path, ".file"))
- objtype = OSTREE_OBJECT_TYPE_FILE;
- else if (g_str_has_suffix (path, ".packfile"))
- {
- objtype = OSTREE_OBJECT_TYPE_FILE;
- packed = TRUE;
- }
- else
- g_assert_not_reached ();
-
- if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
- {
- if (!checksum_packed_file (data, path, &checksum, &error))
- goto out;
- }
- else
- {
- if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
- goto out;
- }
-
- filename_checksum = g_strdup (g_file_info_get_name (file_info));
- dot = strrchr (filename_checksum, '.');
- g_assert (dot != NULL);
- *dot = '\0';
-
- dirname = g_path_get_dirname (path);
- checksum_prefix = g_path_get_basename (dirname);
- checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
-
- if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
- {
- data->had_error = TRUE;
- g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
- path, g_checksum_get_string (checksum));
- }
-
- data->n_objects++;
-
- out:
- if (checksum != NULL)
- g_checksum_free (checksum);
- g_free (dirname);
- g_free (checksum_prefix);
- g_free (checksum_string);
- g_free (filename_checksum);
- if (error != NULL)
- {
- g_printerr ("%s\n", error->message);
- g_clear_error (&error);
- }
-}
-
-gboolean
-ostree_builtin_fsck (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- OtFsckData data;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
-
- context = g_option_context_new ("- Check the repository for consistency");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- data.n_objects = 0;
- data.had_error = FALSE;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
- goto out;
-
- if (data.had_error)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Encountered filesystem consistency errors");
- goto out;
- }
- if (!quiet)
- g_printerr ("Total Objects: %u\n", data.n_objects);
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean archive;
-
-static GOptionEntry options[] = {
- { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
- { NULL }
-};
-
-#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
- "repo_version=0\n")
-
-
-gboolean
-ostree_builtin_init (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context = NULL;
- gboolean ret = FALSE;
- GFile *repodir = NULL;
- GFile *child = NULL;
- GFile *grandchild = NULL;
- GString *config_data = NULL;
-
- context = g_option_context_new ("- Initialize a new empty repository");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repodir = ot_util_new_file_for_path (repo_path);
-
- child = g_file_get_child (repodir, "config");
-
- config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
- g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
- if (!g_file_replace_contents (child,
- config_data->str,
- config_data->len,
- NULL, FALSE, 0, NULL,
- NULL, error))
- goto out;
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "objects");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "tmp");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "refs");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
-
- grandchild = g_file_get_child (child, "heads");
- if (!g_file_make_directory (grandchild, NULL, error))
- goto out;
- g_clear_object (&grandchild);
-
- grandchild = g_file_get_child (child, "remotes");
- if (!g_file_make_directory (grandchild, NULL, error))
- goto out;
- g_clear_object (&grandchild);
-
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "tags");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
- g_clear_object (&child);
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- if (config_data)
- g_string_free (config_data, TRUE);
- g_clear_object (&repodir);
- g_clear_object (&child);
- g_clear_object (&grandchild);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
- { NULL }
-};
-
-gboolean
-ostree_builtin_log (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- GOutputStream *pager = NULL;
- const char *rev;
- GVariant *commit = NULL;
- char *resolved_rev = NULL;
-
- context = g_option_context_new ("- Show revision log");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (argc < 2)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A revision must be specified");
- goto out;
- }
-
- rev = argv[1];
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (!ot_util_spawn_pager (&pager, error))
- goto out;
-
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- while (TRUE)
- {
- OstreeSerializedVariantType type;
- char *formatted = NULL;
- guint32 version;
- const char *parent;
- const char *subject;
- const char *body;
- guint64 timestamp;
- const char *contents;
- const char *root_metadata;
- GDateTime *time_obj = NULL;
- char *formatted_date = NULL;
- const char *body_newline;
- gsize bytes_written;
- GVariant *commit_metadata = NULL;
- char *formatted_metadata = NULL;
-
- if (commit)
- g_variant_unref (commit);
- if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
- goto out;
-
- /* Ignore commit metadata for now */
- g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
- &version, &commit_metadata, &parent, &subject, &body,
- ×tamp, &contents, &root_metadata);
- version = GUINT32_FROM_BE (version);
- timestamp = GUINT64_FROM_BE (timestamp);
- time_obj = g_date_time_new_from_unix_utc (timestamp);
- formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
- g_date_time_unref (time_obj);
- time_obj = NULL;
-
- formatted_metadata = g_variant_print (commit_metadata, TRUE);
- g_variant_unref (commit_metadata);
- formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
- resolved_rev, subject, formatted_date, formatted_metadata);
- g_free (formatted_metadata);
- g_free (formatted_date);
- formatted_date = NULL;
-
- if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
- {
- g_free (formatted);
- goto out;
- }
- g_free (formatted);
-
- body_newline = strchr (body, '\n');
- do {
- gsize len;
- if (!g_output_stream_write_all (pager, " ", 4, &bytes_written, NULL, error))
- goto out;
- len = body_newline ? body_newline - body : strlen (body);
- if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
- goto out;
- if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
- goto out;
- body_newline = strchr (body, '\n');
- if (!body_newline)
- break;
- else
- body_newline += 1;
- } while (*body_newline);
-
- if (strcmp (parent, "") == 0)
- break;
- g_free (resolved_rev);
- resolved_rev = g_strdup (parent);
- }
-
- if (!g_output_stream_close (pager, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- g_free (resolved_rev);
- if (context)
- g_option_context_free (context);
- if (commit)
- g_variant_unref (commit);
- g_clear_object (&repo);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-#include <libsoup/soup-gnome.h>
-
-static GOptionEntry options[] = {
- { NULL }
-};
-
-static gboolean
-fetch_uri (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *uri,
- char **temp_filename,
- GError **error)
-{
- gboolean ret = FALSE;
- SoupMessage *msg = NULL;
- guint response;
- char *template = NULL;
- int fd;
- SoupBuffer *buf = NULL;
- GFile *tempf = NULL;
-
- msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
-
- response = soup_session_send_message (soup, msg);
- if (response != 200)
- {
- char *uri_string = soup_uri_to_string (uri, FALSE);
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Failed to retrieve '%s': %d %s",
- uri_string, response, msg->reason_phrase);
- g_free (uri_string);
- goto out;
- }
-
- template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
-
- fd = g_mkstemp (template);
- if (fd < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- close (fd);
- tempf = ot_util_new_file_for_path (template);
-
- buf = soup_message_body_flatten (msg->response_body);
-
- if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
- goto out;
-
- *temp_filename = template;
- template = NULL;
-
- ret = TRUE;
- out:
- g_free (template);
- g_clear_object (&msg);
- g_clear_object (&tempf);
- return ret;
-}
-
-static gboolean
-store_object (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *baseuri,
- const char *object,
- OstreeObjectType objtype,
- gboolean *did_exist,
- GError **error)
-{
- gboolean ret = FALSE;
- char *filename = NULL;
- char *objpath = NULL;
- char *relpath = NULL;
- SoupURI *obj_uri = NULL;
-
- objpath = ostree_get_relative_object_path (object, objtype, TRUE);
- obj_uri = soup_uri_copy (baseuri);
- relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
- soup_uri_set_path (obj_uri, relpath);
-
- if (!fetch_uri (repo, soup, obj_uri, &filename, error))
- goto out;
-
- if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
- goto out;
-
- ret = TRUE;
- out:
- if (obj_uri)
- soup_uri_free (obj_uri);
- if (filename)
- (void) unlink (filename);
- g_free (filename);
- g_free (objpath);
- g_free (relpath);
- return ret;
-}
-
-static gboolean
-store_tree_recurse (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *base_uri,
- const char *rev,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *tree = NULL;
- GVariant *files_variant = NULL;
- GVariant *dirs_variant = NULL;
- OstreeSerializedVariantType metatype;
- gboolean did_exist;
- int i, n;
-
- if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
-
- if (!did_exist)
- {
- if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
- goto out;
-
- if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Tree metadata '%s' has wrong type %d, expected %d",
- rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
- goto out;
- }
-
- /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
- g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
- g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
-
- n = g_variant_n_children (files_variant);
- for (i = 0; i < n; i++)
- {
- const char *filename;
- const char *checksum;
-
- g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
-
- if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
- goto out;
- }
-
- n = g_variant_n_children (dirs_variant);
- for (i = 0; i < n; i++)
- {
- const char *dirname;
- const char *tree_checksum;
- const char *meta_checksum;
-
- g_variant_get_child (dirs_variant, i, "(sss)",
- &dirname, &tree_checksum, &meta_checksum);
-
- if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
- goto out;
-
- if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
- }
- }
-
- ret = TRUE;
- out:
- if (tree)
- g_variant_unref (tree);
- if (files_variant)
- g_variant_unref (files_variant);
- if (dirs_variant)
- g_variant_unref (dirs_variant);
- return ret;
-}
-
-static gboolean
-store_commit_recurse (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *base_uri,
- const char *rev,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *commit = NULL;
- OstreeSerializedVariantType metatype;
- const char *tree_contents_checksum;
- const char *tree_meta_checksum;
- gboolean did_exist;
-
- if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
-
- if (!did_exist)
- {
- if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
- goto out;
-
- if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Commit '%s' has wrong type %d, expected %d",
- rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
- goto out;
- }
-
- /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
- g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
- g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
-
- if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
-
- if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- if (commit)
- g_variant_unref (commit);
- return ret;
-}
-
-gboolean
-ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *remote;
- const char *branch;
- char *remote_branch_ref_path = NULL;
- char *key = NULL;
- char *baseurl = NULL;
- char *refpath = NULL;
- char *temppath = NULL;
- GKeyFile *config = NULL;
- SoupURI *base_uri = NULL;
- SoupURI *target_uri = NULL;
- SoupSession *soup = NULL;
- char *rev = NULL;
-
- context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 3)
- {
- ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
- goto out;
- }
-
- remote = argv[1];
- branch = argv[2];
-
- config = ostree_repo_get_config (repo);
-
- key = g_strdup_printf ("remote \"%s\"", remote);
- baseurl = g_key_file_get_string (config, key, "url", error);
- if (!baseurl)
- goto out;
- base_uri = soup_uri_new (baseurl);
- if (!base_uri)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Failed to parse url '%s'", baseurl);
- goto out;
- }
- target_uri = soup_uri_copy (base_uri);
- g_free (refpath);
- refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
- soup_uri_set_path (target_uri, refpath);
-
- soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
- NULL);
- if (!fetch_uri (repo, soup, target_uri, &temppath, error))
- goto out;
-
- rev = ot_util_get_file_contents_utf8 (temppath, error);
- if (!rev)
- goto out;
- g_strchomp (rev);
-
- if (!ostree_validate_checksum_string (rev, error))
- goto out;
-
- if (!store_commit_recurse (repo, soup, base_uri, rev, error))
- goto out;
-
- if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- if (temppath)
- (void) unlink (temppath);
- g_free (temppath);
- g_free (key);
- g_free (rev);
- g_free (baseurl);
- g_free (refpath);
- g_free (remote_branch_ref_path);
- g_clear_object (&soup);
- if (base_uri)
- soup_uri_free (base_uri);
- if (target_uri)
- soup_uri_free (target_uri);
- g_clear_object (&repo);
- g_clear_object (&soup);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
- { NULL }
-};
-
-static void
-usage_error (GOptionContext *context, const char *message, GError **error)
-{
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- message);
-}
-
-gboolean
-ostree_builtin_remote (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *op;
- GKeyFile *config = NULL;
-
- context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 2)
- {
- usage_error (context, "OPERATION must be specified", error);
- goto out;
- }
-
- op = argv[1];
-
- config = ostree_repo_copy_config (repo);
-
- if (!strcmp (op, "add"))
- {
- char *key;
- if (argc < 4)
- {
- usage_error (context, "NAME and URL must be specified", error);
- goto out;
- }
- key = g_strdup_printf ("remote \"%s\"", argv[2]);
- g_key_file_set_string (config, key, "url", argv[3]);
- g_free (key);
- }
- else
- {
- usage_error (context, "Unknown operation", error);
- goto out;
- }
-
- if (!ostree_repo_write_config (repo, config, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- if (config)
- g_key_file_free (config);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
- { NULL }
-};
-
-gboolean
-ostree_builtin_rev_parse (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *rev = "master";
- char *resolved_rev = NULL;
- GVariant *variant = NULL;
- char *formatted_variant = NULL;
-
- context = g_option_context_new ("REV - Output the target of a rev");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 2)
- {
- ot_util_usage_error (context, "REV must be specified", error);
- goto out;
- }
- rev = argv[1];
-
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- g_print ("%s\n", resolved_rev);
-
- ret = TRUE;
- out:
- g_free (resolved_rev);
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- if (variant)
- g_variant_unref (variant);
- g_free (formatted_variant);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean quiet;
-
-static GOptionEntry options[] = {
- { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
- { NULL }
-};
-
-gboolean
-ostree_builtin_run_triggers (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *dir;
-
- context = g_option_context_new ("DIR - Run trigger scripts for directory");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 1)
- {
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "DIR must be specified");
- goto out;
- }
-
- dir = argv[1];
-
- checkout = ostree_checkout_new (repo, dir);
- if (!ostree_checkout_run_triggers (checkout, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean print_compose;
-static char* print_variant_type;
-
-static GOptionEntry options[] = {
- { "print-compose", 0, 0, G_OPTION_ARG_NONE, &print_compose, "If given, show the branches which make up the given compose commit", NULL },
- { "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &print_variant_type, "If given, argument should be a filename and it will be interpreted as this type", NULL },
- { NULL }
-};
-
-static void
-print_variant (GVariant *variant)
-{
- char *formatted_variant = NULL;
-
- formatted_variant = g_variant_print (variant, TRUE);
- g_print ("%s\n", formatted_variant);
-
- g_free (formatted_variant);
-}
-
-static gboolean
-do_print_variant_generic (const GVariantType *type,
- const char *filename,
- GError **error)
-{
- gboolean ret = FALSE;
- GFile *f = NULL;
- GVariant *variant = NULL;
-
- f = ot_util_new_file_for_path (filename);
-
- if (!ot_util_variant_map (f, type, &variant, error))
- goto out;
-
- print_variant (variant);
-
- ret = TRUE;
- out:
- if (variant)
- g_variant_unref (variant);
- g_clear_object (&f);
- return ret;
-}
-
-static gboolean
-show_repo_meta (OstreeRepo *repo,
- const char *rev,
- const char *resolved_rev,
- GError **error)
-{
- OstreeSerializedVariantType type;
- gboolean ret = FALSE;
- GVariant *variant = NULL;
-
- if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
- goto out;
- g_print ("Object: %s\nType: %d\n", resolved_rev, type);
- print_variant (variant);
-
- ret = TRUE;
- out:
- if (variant)
- g_variant_unref (variant);
- return ret;
-}
-
-static gboolean
-do_print_compose (OstreeRepo *repo,
- const char *rev,
- const char *resolved_rev,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *variant = NULL;
- GVariant *metadata = NULL;
- GVariant *compose_contents = NULL;
- GVariantIter *viter = NULL;
- GHashTable *metadata_hash = NULL;
- const char *branch;
- const char *branchrev;
-
- if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
- resolved_rev, &variant, error))
- goto out;
-
- /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
- metadata = g_variant_get_child_value (variant, 1);
- metadata_hash = ot_util_variant_asv_to_hash_table (metadata);
-
- compose_contents = g_hash_table_lookup (metadata_hash, "ostree-compose");
- if (!compose_contents)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Commit %s does not have compose metadata key \"ostree-compose\"", resolved_rev);
- goto out;
- }
-
- g_variant_get_child (compose_contents, 0, "a(ss)", &viter);
- while (g_variant_iter_next (viter, "(&s&s)", &branch, &branchrev))
- {
- g_print ("%s %s\n", branch, branchrev);
- }
-
- ret = TRUE;
- out:
- if (variant)
- g_variant_unref (variant);
- if (viter)
- g_variant_iter_free (viter);
- if (metadata)
- g_variant_unref (metadata);
- if (metadata_hash)
- g_hash_table_destroy (metadata_hash);
- return ret;
-}
-
-gboolean
-ostree_builtin_show (int argc, char **argv, const char *repo_path, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *rev = "master";
- char *resolved_rev = NULL;
-
- context = g_option_context_new ("- Output a metadata object");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc > 1)
- rev = argv[1];
-
- if (print_compose)
- {
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- if (!do_print_compose (repo, rev, resolved_rev, error))
- goto out;
- }
- else if (print_variant_type)
- {
- if (!do_print_variant_generic (G_VARIANT_TYPE (print_variant_type), rev, error))
- goto out;
- }
- else
- {
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- if (!show_repo_meta (repo, rev, resolved_rev, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- g_free (resolved_rev);
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_BUILTINS__
-#define __OSTREE_BUILTINS__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
- OSTREE_BUILTIN_FLAG_NONE = 0,
-} OstreeBuiltinFlags;
-
-typedef struct {
- const char *name;
- gboolean (*fn) (int argc, char **argv, const char *repo, GError **error);
- int flags; /* OstreeBuiltinFlags */
-} OstreeBuiltin;
-
-gboolean ostree_builtin_checkout (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_commit (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_compose (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_diff (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_init (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_log (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_pull (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_fsck (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_show (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_remote (int argc, char **argv, const char *repo, GError **error);
-
-G_END_DECLS
-
-#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+enum {
+ PROP_0,
+
+ PROP_REPO,
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
+
+typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
+
+struct _OstreeCheckoutPrivate {
+ OstreeRepo *repo;
+ char *path;
+};
+
+static void
+ostree_checkout_finalize (GObject *object)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->path);
+ g_clear_object (&priv->repo);
+
+ G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
+}
+
+static void
+ostree_checkout_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ priv->path = g_value_dup_string (value);
+ break;
+ case PROP_REPO:
+ priv->repo = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_checkout_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ case PROP_REPO:
+ g_value_set_object (value, priv->repo);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ostree_checkout_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+ OstreeCheckoutPrivate *priv;
+
+ parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ priv = GET_PRIVATE (object);
+
+ g_assert (priv->path != NULL);
+
+ return object;
+}
+
+static void
+ostree_checkout_class_init (OstreeCheckoutClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
+
+ object_class->constructor = ostree_checkout_constructor;
+ object_class->get_property = ostree_checkout_get_property;
+ object_class->set_property = ostree_checkout_set_property;
+ object_class->finalize = ostree_checkout_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path", "", "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_REPO,
+ g_param_spec_object ("repo", "", "",
+ OSTREE_TYPE_REPO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_checkout_init (OstreeCheckout *self)
+{
+}
+
+OstreeCheckout*
+ostree_checkout_new (OstreeRepo *repo,
+ const char *path)
+{
+ return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
+}
+
+static gboolean
+executable_exists_in_checkout (const char *path,
+ const char *executable)
+{
+ int i;
+ const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
+
+ for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
+ {
+ char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
+ gboolean exists;
+
+ exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
+ g_free (possible_path);
+
+ if (exists)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+run_trigger (OstreeCheckout *self,
+ GFile *trigger,
+ gboolean requires_chroot,
+ GError **error)
+{
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *path = NULL;
+ char *temp_path = NULL;
+ char *rel_temp_path = NULL;
+ GFile *temp_copy = NULL;
+ char *basename = NULL;
+ GPtrArray *args = NULL;
+ int estatus;
+
+ path = g_file_get_path (trigger);
+ basename = g_path_get_basename (path);
+
+ args = g_ptr_array_new ();
+
+ if (requires_chroot)
+ {
+ temp_path = g_build_filename (priv->path, basename, NULL);
+ rel_temp_path = g_strconcat ("./", basename, NULL);
+ temp_copy = ot_util_new_file_for_path (temp_path);
+
+ if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
+ goto out;
+
+ g_ptr_array_add (args, "chroot");
+ g_ptr_array_add (args, ".");
+ g_ptr_array_add (args, rel_temp_path);
+ g_ptr_array_add (args, NULL);
+ }
+ else
+ {
+ g_ptr_array_add (args, path);
+ g_ptr_array_add (args, NULL);
+ }
+
+ g_print ("Running trigger: %s\n", path);
+ if (!g_spawn_sync (priv->path,
+ (char**)args->pdata,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, NULL,
+ &estatus,
+ error))
+ {
+ g_prefix_error (error, "Failed to run trigger %s: ", basename);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (requires_chroot && temp_path)
+ (void)unlink (temp_path);
+
+ g_free (path);
+ g_free (basename);
+ g_free (temp_path);
+ g_free (rel_temp_path);
+ g_clear_object (&temp_copy);
+ if (args)
+ g_ptr_array_free (args, TRUE);
+ return ret;
+}
+
+static gboolean
+check_trigger (OstreeCheckout *self,
+ GFile *trigger,
+ GError **error)
+{
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GInputStream *instream = NULL;
+ GDataInputStream *datain = NULL;
+ GError *temp_error = NULL;
+ char *line;
+ gsize len;
+ gboolean requires_chroot = TRUE;
+ gboolean matches = FALSE;
+
+ instream = (GInputStream*)g_file_read (trigger, NULL, error);
+ if (!instream)
+ goto out;
+ datain = g_data_input_stream_new (instream);
+
+ while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
+ {
+ if (g_str_has_prefix (line, "# IfExecutable: "))
+ {
+ char *executable = g_strdup (line + strlen ("# IfExecutable: "));
+ g_strchomp (executable);
+ matches = executable_exists_in_checkout (priv->path, executable);
+ g_free (executable);
+ }
+
+ g_free (line);
+ }
+ if (line == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (matches)
+ {
+ if (!run_trigger (self, trigger, requires_chroot, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&instream);
+ g_clear_object (&datain);
+ return ret;
+}
+
+gboolean
+ostree_checkout_run_triggers (OstreeCheckout *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ char *triggerdir_path = NULL;
+ GFile *triggerdir = NULL;
+ GFileInfo *file_info = NULL;
+ GFileEnumerator *enumerator = NULL;
+
+ triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
+ triggerdir = ot_util_new_file_for_path (triggerdir_path);
+
+ enumerator = g_file_enumerate_children (triggerdir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+ char *child_path = NULL;
+ GFile *child = NULL;
+ gboolean success;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
+ {
+ child_path = g_build_filename (triggerdir_path, name, NULL);
+ child = ot_util_new_file_for_path (child_path);
+
+ success = check_trigger (self, child, error);
+ }
+ else
+ success = TRUE;
+
+ g_object_unref (file_info);
+ g_free (child_path);
+ g_clear_object (&child);
+ if (!success)
+ goto out;
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (triggerdir_path);
+ g_clear_object (&triggerdir);
+ g_clear_object (&enumerator);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CHECKOUT
+#define _OSTREE_CHECKOUT
+
+#include <ostree-repo.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
+#define OSTREE_CHECKOUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
+#define OSTREE_CHECKOUT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+#define OSTREE_IS_CHECKOUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_IS_CHECKOUT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_CHECKOUT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+
+typedef struct {
+ GObject parent;
+} OstreeCheckout;
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeCheckoutClass;
+
+GType ostree_checkout_get_type (void);
+
+OstreeCheckout* ostree_checkout_new (OstreeRepo *repo,
+ const char *path);
+
+gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_CHECKOUT */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+gboolean
+ostree_validate_checksum_string (const char *sha256,
+ GError **error)
+{
+ if (strlen (sha256) != 64)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid rev '%s'", sha256);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
+{
+ guint32 perms = (mode & ~S_IFMT);
+ g_checksum_update (checksum, (guint8*) &uid, 4);
+ g_checksum_update (checksum, (guint8*) &gid, 4);
+ g_checksum_update (checksum, (guint8*) &perms, 4);
+}
+
+static char *
+canonicalize_xattrs (char *xattr_string, size_t len)
+{
+ char *p;
+ GSList *xattrs = NULL;
+ GSList *iter;
+ GString *result;
+
+ result = g_string_new (0);
+
+ p = xattr_string;
+ while (p < xattr_string+len)
+ {
+ xattrs = g_slist_prepend (xattrs, p);
+ p += strlen (p) + 1;
+ }
+
+ xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+ for (iter = xattrs; iter; iter = iter->next)
+ g_string_append (result, iter->data);
+
+ g_slist_free (xattrs);
+ return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+ const char *xattrs,
+ size_t len,
+ GVariantBuilder *builder,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ const char *p;
+
+ p = xattrs;
+ while (p < xattrs+len)
+ {
+ ssize_t bytes_read;
+ char *buf;
+
+ bytes_read = lgetxattr (path, p, NULL, 0);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ if (bytes_read == 0)
+ continue;
+
+ buf = g_malloc (bytes_read);
+ if (lgetxattr (path, p, buf, bytes_read) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (buf);
+ goto out;
+ }
+
+ g_variant_builder_add (builder, "(@ay@ay)",
+ g_variant_new_bytestring (p),
+ g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+ buf, bytes_read, FALSE, g_free, buf));
+
+ p = p + strlen (p) + 1;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+GVariant *
+ostree_get_xattrs_for_path (const char *path,
+ GError **error)
+{
+ GVariant *ret = NULL;
+ GVariantBuilder builder;
+ char *xattr_names = NULL;
+ char *xattr_names_canonical = NULL;
+ ssize_t bytes_read;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+
+ bytes_read = llistxattr (path, NULL, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno != ENOTSUP)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ xattr_names = g_malloc (bytes_read);
+ if (llistxattr (path, xattr_names, bytes_read) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+
+ if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
+ goto out;
+ }
+
+ ret = g_variant_builder_end (&builder);
+ g_variant_ref_sink (ret);
+ out:
+ if (!ret)
+ g_variant_builder_clear (&builder);
+ g_free (xattr_names);
+ g_free (xattr_names_canonical);
+ return ret;
+}
+
+gboolean
+ostree_stat_and_checksum_file (int dir_fd, const char *path,
+ OstreeObjectType objtype,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error)
+{
+ GChecksum *content_sha256 = NULL;
+ GChecksum *content_and_meta_sha256 = NULL;
+ char *stat_string = NULL;
+ ssize_t bytes_read;
+ GVariant *xattrs = NULL;
+ int fd = -1;
+ DIR *temp_dir = NULL;
+ char *basename = NULL;
+ gboolean ret = FALSE;
+ char *symlink_target = NULL;
+ char *device_id = NULL;
+ struct stat stbuf;
+
+ basename = g_path_get_basename (path);
+
+ if (dir_fd == -1)
+ {
+ char *dirname = g_path_get_dirname (path);
+ temp_dir = opendir (dirname);
+ if (temp_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (dirname);
+ }
+ g_free (dirname);
+ dir_fd = dirfd (temp_dir);
+ }
+
+ if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (S_ISREG(stbuf.st_mode))
+ {
+ fd = ot_util_open_file_read_at (dir_fd, basename, error);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ if (objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+ }
+
+ content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
+
+ if (S_ISREG(stbuf.st_mode))
+ {
+ guint8 buf[8192];
+
+ while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
+ g_checksum_update (content_sha256, buf, bytes_read);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISLNK(stbuf.st_mode))
+ {
+ symlink_target = g_malloc (PATH_MAX);
+
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+
+ bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
+ }
+ else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
+ {
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+ device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
+ g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
+ }
+ else if (S_ISFIFO(stbuf.st_mode))
+ {
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unsupported file '%s' (must be regular, symbolic link, fifo, or character/block device)",
+ path);
+ goto out;
+ }
+
+ content_and_meta_sha256 = g_checksum_copy (content_sha256);
+
+ if (objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
+ stbuf.st_gid, stbuf.st_mode);
+ g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+ }
+
+ *out_stbuf = stbuf;
+ *out_checksum = content_and_meta_sha256;
+ ret = TRUE;
+ out:
+ if (fd >= 0)
+ close (fd);
+ if (temp_dir != NULL)
+ closedir (temp_dir);
+ g_free (symlink_target);
+ g_free (basename);
+ g_free (stat_string);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ if (content_sha256)
+ g_checksum_free (content_sha256);
+ return ret;
+}
+
+gboolean
+ostree_set_xattrs (const char *path, GVariant *xattrs, GCancellable *cancellable, GError **error)
+{
+ gboolean ret = FALSE;
+ int i, n;
+
+ n = g_variant_n_children (xattrs);
+ for (i = 0; i < n; i++)
+ {
+ const guint8* name;
+ GVariant *value;
+ const guint8* value_data;
+ gsize value_len;
+ gboolean loop_err;
+
+ g_variant_get_child (xattrs, i, "(^&ay@ay)",
+ &name, &value);
+ value_data = g_variant_get_fixed_array (value, &value_len, 1);
+
+ loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
+
+ g_variant_unref (value);
+ if (loop_err)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+gboolean
+ostree_parse_metadata_file (const char *path,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error)
+{
+ GFile *pathf = NULL;
+ gboolean ret = FALSE;
+ GVariant *ret_variant = NULL;
+ GVariant *container = NULL;
+ guint32 ret_type;
+
+ pathf = ot_util_new_file_for_path (path);
+ if (!ot_util_variant_map (pathf, G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
+ &container, error))
+ goto out;
+
+ g_variant_get (container, "(uv)",
+ &ret_type, &ret_variant);
+ ret_type = GUINT32_FROM_BE (ret_type);
+ if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_type = ret_type;
+ *out_variant = ot_util_variant_take_ref (ret_variant);
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ if (container != NULL)
+ g_variant_unref (container);
+ g_clear_object (&pathf);
+ return ret;
+}
+
+char *
+ostree_get_relative_object_path (const char *checksum,
+ OstreeObjectType type,
+ gboolean archive)
+{
+ GString *path;
+ const char *type_string;
+
+ g_assert (strlen (checksum) == 64);
+
+ path = g_string_new ("objects/");
+
+ g_string_append_len (path, checksum, 2);
+ g_string_append_c (path, '/');
+ g_string_append (path, checksum + 2);
+ switch (type)
+ {
+ case OSTREE_OBJECT_TYPE_FILE:
+ if (archive)
+ type_string = ".packfile";
+ else
+ type_string = ".file";
+ break;
+ case OSTREE_OBJECT_TYPE_META:
+ type_string = ".meta";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ g_string_append (path, type_string);
+ return g_string_free (path, FALSE);
+}
+
+gboolean
+ostree_pack_object (GOutputStream *output,
+ GFile *file,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *path = NULL;
+ GFileInfo *finfo = NULL;
+ GFileInputStream *instream = NULL;
+ gboolean pack_builder_initialized = FALSE;
+ GVariantBuilder pack_builder;
+ GVariant *pack_variant = NULL;
+ GVariant *xattrs = NULL;
+ gsize bytes_written;
+
+ path = g_file_get_path (file);
+
+ finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+ if (!finfo)
+ goto out;
+
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ {
+ guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
+ if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
+ goto out;
+
+ instream = g_file_read (file, NULL, error);
+ if (!instream)
+ goto out;
+
+ if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
+ goto out;
+ }
+ else
+ {
+ guint32 uid, gid, mode;
+ guint32 device = 0;
+ guint32 metadata_size_be;
+ const char *target = NULL;
+ guint64 object_size;
+
+ uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
+ gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
+ mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
+
+ g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
+ pack_builder_initialized = TRUE;
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
+
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+ g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
+
+ if (S_ISREG (mode))
+ {
+ object_size = (guint64)g_file_info_get_size (finfo);
+ }
+ else if (S_ISLNK (mode))
+ {
+ target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
+ object_size = strlen (target);
+ }
+ else if (S_ISBLK (mode) || S_ISCHR (mode))
+ {
+ device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
+ object_size = 4;
+ }
+ else if (S_ISFIFO (mode))
+ {
+ object_size = 0;
+ }
+ else
+ g_assert_not_reached ();
+
+ g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
+ pack_variant = g_variant_builder_end (&pack_builder);
+ pack_builder_initialized = FALSE;
+
+ metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
+
+ if (!g_output_stream_write_all (output, &metadata_size_be, 4,
+ &bytes_written, cancellable, error))
+ goto out;
+ g_assert (bytes_written == 4);
+
+ if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
+ &bytes_written, cancellable, error))
+ goto out;
+
+ if (S_ISREG (mode))
+ {
+ instream = g_file_read (file, NULL, error);
+ if (!instream)
+ goto out;
+ bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
+ if (bytes_written < 0)
+ goto out;
+ if (bytes_written != object_size)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File size changed unexpectedly");
+ goto out;
+ }
+ }
+ else if (S_ISLNK (mode))
+ {
+ if (!g_output_stream_write_all (output, target, object_size,
+ &bytes_written, cancellable, error))
+ goto out;
+ }
+ else if (S_ISBLK (mode) || S_ISCHR (mode))
+ {
+ guint32 device_be = GUINT32_TO_BE (device);
+ g_assert (object_size == 4);
+ if (!g_output_stream_write_all (output, &device_be, object_size,
+ &bytes_written, cancellable, error))
+ goto out;
+ g_assert (bytes_written == 4);
+ }
+ else if (S_ISFIFO (mode))
+ {
+ }
+ else
+ g_assert_not_reached ();
+ }
+
+ ret = TRUE;
+ out:
+ g_free (path);
+ g_clear_object (&finfo);
+ g_clear_object (&instream);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ if (pack_builder_initialized)
+ g_variant_builder_clear (&pack_builder);
+ if (pack_variant)
+ g_variant_unref (pack_variant);
+ return ret;
+}
+
+static gboolean
+splice_and_checksum (GOutputStream *out,
+ GInputStream *in,
+ GChecksum *checksum,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (checksum != NULL)
+ {
+ gsize bytes_read, bytes_written;
+ char buf[4096];
+ do
+ {
+ if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
+ goto out;
+ if (checksum)
+ g_checksum_update (checksum, (guint8*)buf, bytes_read);
+ if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
+ goto out;
+ }
+ while (bytes_read > 0);
+ }
+ else
+ {
+ if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+unpack_meta (const char *path,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ GFile *dest_file = NULL;
+ GFileInputStream *in = NULL;
+ GChecksum *ret_checksum = NULL;
+ GFileOutputStream *out = NULL;
+
+ file = ot_util_new_file_for_path (path);
+ dest_file = ot_util_new_file_for_path (dest_path);
+
+ if (out_checksum)
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ if (out_checksum)
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (!ret)
+ (void) unlink (dest_path);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_clear_object (&file);
+ g_clear_object (&dest_file);
+ g_clear_object (&in);
+ return ret;
+}
+
+gboolean
+ostree_parse_packed_file (GFile *file,
+ GVariant **out_metadata,
+ GInputStream **out_content,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *metadata_buf = NULL;
+ GVariant *ret_metadata = NULL;
+ GFileInputStream *in = NULL;
+ guint32 metadata_len;
+ gsize bytes_read;
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != 4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading metadata length");
+ goto out;
+ }
+
+ metadata_len = GUINT32_FROM_BE (metadata_len);
+ if (metadata_len > OSTREE_MAX_METADATA_SIZE)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; metadata length %u is larger than maximum %u",
+ metadata_len, OSTREE_MAX_METADATA_SIZE);
+ goto out;
+ }
+ metadata_buf = g_malloc (metadata_len);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != metadata_len)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading metadata");
+ goto out;
+ }
+
+ ret_metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE,
+ (GDestroyNotify)g_free,
+ metadata_buf);
+ metadata_buf = NULL;
+
+ ret = TRUE;
+ *out_metadata = ret_metadata;
+ ret_metadata = NULL;
+ *out_content = (GInputStream*)in;
+ in = NULL;
+ out:
+ g_clear_object (&in);
+ if (ret_metadata)
+ g_variant_unref (ret_metadata);
+ return ret;
+}
+
+static gboolean
+unpack_file (const char *path,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ GFile *dest_file = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GInputStream *in = NULL;
+ GFileOutputStream *out = NULL;
+ GChecksum *ret_checksum = NULL;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read;
+
+ file = ot_util_new_file_for_path (path);
+
+ if (!ostree_parse_packed_file (file, &metadata, &in, NULL, error))
+ goto out;
+
+ g_variant_get (metadata, "(uuuu@a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ &xattrs, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ dest_file = ot_util_new_file_for_path (dest_path);
+
+ if (out_checksum)
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ if (S_ISREG (mode))
+ {
+ out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!splice_and_checksum ((GOutputStream*)out, in, ret_checksum, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+ }
+ else if (S_ISLNK (mode))
+ {
+ char target[PATH_MAX+1];
+
+ if (!g_input_stream_read_all (in, target, sizeof(target)-1, &bytes_read, NULL, error))
+ goto out;
+ target[bytes_read] = '\0';
+ if (ret_checksum)
+ g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
+ if (symlink (target, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISCHR (mode) || S_ISBLK (mode))
+ {
+ guint32 dev;
+
+ if (!g_input_stream_read_all (in, &dev, 4, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != 4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading device id");
+ goto out;
+ }
+ dev = GUINT32_FROM_BE (dev);
+ if (ret_checksum)
+ g_checksum_update (ret_checksum, (guint8*)&dev, 4);
+ if (mknod (dest_path, mode, dev) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISFIFO (mode))
+ {
+ if (mkfifo (dest_path, mode) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; invalid mode %u", mode);
+ goto out;
+ }
+
+ if (!S_ISLNK (mode))
+ {
+ if (chmod (dest_path, mode) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ if (!ostree_set_xattrs (dest_path, xattrs, NULL, error))
+ goto out;
+
+ if (ret_checksum)
+ {
+ ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+ g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+ }
+
+ ret = TRUE;
+ if (out_checksum)
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (!ret)
+ (void) unlink (dest_path);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_clear_object (&file);
+ g_clear_object (&dest_file);
+ g_clear_object (&in);
+ g_clear_object (&out);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+gboolean
+ostree_unpack_object (const char *path,
+ OstreeObjectType objtype,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ return unpack_meta (path, dest_path, out_checksum, error);
+ else
+ return unpack_file (path, dest_path, out_checksum, error);
+}
+
+
+
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CORE
+#define _OSTREE_CORE
+
+#include <otutil.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_MAX_METADATA_SIZE (1 << 26)
+
+#define OSTREE_GIO_FAST_QUERYINFO "standard::name,standard::type,standard::is-symlink,standard::symlink-target,standard::is-hidden,unix::*"
+
+#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+typedef enum {
+ OSTREE_OBJECT_TYPE_FILE = 1,
+ OSTREE_OBJECT_TYPE_META = 2,
+} OstreeObjectType;
+
+typedef enum {
+ OSTREE_SERIALIZED_TREE_VARIANT = 1,
+ OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
+ OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
+ OSTREE_SERIALIZED_XATTR_VARIANT = 4
+} OstreeSerializedVariantType;
+#define OSTREE_SERIALIZED_VARIANT_LAST 4
+
+#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
+
+/*
+ * xattr objects:
+ * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
+ */
+#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
+
+#define OSTREE_DIR_META_VERSION 0
+/*
+ * dirmeta objects:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ */
+#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
+
+#define OSTREE_TREE_VERSION 0
+/*
+ * Tree objects:
+ * u - Version
+ * a{sv} - Metadata
+ * a(ss) - array of (filename, checksum) for files
+ * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
+ */
+#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
+
+#define OSTREE_COMMIT_VERSION 0
+/*
+ * Commit objects:
+ * u - Version
+ * a{sv} - Metadata
+ * s - parent checksum (empty string for initial)
+ * s - subject
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * s - Root tree contents
+ * s - Root tree metadata
+ */
+#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
+
+gboolean ostree_validate_checksum_string (const char *sha256,
+ GError **error);
+
+char *ostree_get_relative_object_path (const char *checksum,
+ OstreeObjectType type,
+ gboolean archive);
+
+GVariant *ostree_get_xattrs_for_path (const char *path,
+ GError **error);
+
+gboolean ostree_set_xattrs (const char *path, GVariant *xattrs,
+ GCancellable *cancellable, GError **error);
+
+gboolean ostree_parse_metadata_file (const char *path,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
+ OstreeObjectType type,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error);
+
+/* Packed files:
+ *
+ * guint32 metadata_length [metadata gvariant] [content]
+ *
+ * metadata variant:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ * t - content length
+ *
+ * And then following the end of the variant is the content. If
+ * symlink, then this is the target; if device, then device ID as
+ * network byte order uint32.
+ */
+#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
+
+gboolean ostree_pack_object (GOutputStream *output,
+ GFile *file,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ostree_parse_packed_file (GFile *file,
+ GVariant **out_metadata,
+ GInputStream **out_content,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ostree_unpack_object (const char *path,
+ OstreeObjectType objtype,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error);
+
+void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
+
+
+#endif /* _OSTREE_REPO */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+#include <string.h>
+
+struct _OstreeRepoFileEnumerator
+{
+ GFileEnumerator parent;
+
+ OstreeRepoFile *dir;
+ char *attributes;
+ GFileQueryInfoFlags flags;
+
+ int index;
+};
+
+#define ostree_repo_file_enumerator_get_type _ostree_repo_file_enumerator_get_type
+G_DEFINE_TYPE (OstreeRepoFileEnumerator, ostree_repo_file_enumerator, G_TYPE_FILE_ENUMERATOR);
+
+static GFileInfo *ostree_repo_file_enumerator_next_file (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean ostree_repo_file_enumerator_close (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error);
+
+
+static void
+ostree_repo_file_enumerator_dispose (GObject *object)
+{
+ OstreeRepoFileEnumerator *self;
+
+ self = OSTREE_REPO_FILE_ENUMERATOR (object);
+
+ g_clear_object (&self->dir);
+ g_free (self->attributes);
+
+ if (G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose)
+ G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_enumerator_finalize (GObject *object)
+{
+ OstreeRepoFileEnumerator *self;
+
+ self = OSTREE_REPO_FILE_ENUMERATOR (object);
+ (void)self;
+
+ G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->finalize (object);
+}
+
+
+static void
+ostree_repo_file_enumerator_class_init (OstreeRepoFileEnumeratorClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
+
+ gobject_class->finalize = ostree_repo_file_enumerator_finalize;
+ gobject_class->dispose = ostree_repo_file_enumerator_dispose;
+
+ enumerator_class->next_file = ostree_repo_file_enumerator_next_file;
+ enumerator_class->close_fn = ostree_repo_file_enumerator_close;
+}
+
+static void
+ostree_repo_file_enumerator_init (OstreeRepoFileEnumerator *self)
+{
+}
+
+GFileEnumerator *
+_ostree_repo_file_enumerator_new (OstreeRepoFile *dir,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFileEnumerator *self;
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE_ENUMERATOR,
+ "container", dir,
+ NULL);
+
+ self->dir = g_object_ref (dir);
+ self->attributes = g_strdup (attributes);
+ self->flags = flags;
+
+ return G_FILE_ENUMERATOR (self);
+}
+
+static GFileInfo *
+ostree_repo_file_enumerator_next_file (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFileEnumerator *self = OSTREE_REPO_FILE_ENUMERATOR (enumerator);
+ gboolean ret = FALSE;
+ GFileInfo *info = NULL;
+
+ if (!_ostree_repo_file_tree_query_child (self->dir, self->index,
+ self->attributes, self->flags,
+ &info, cancellable, error))
+ goto out;
+
+ self->index++;
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ g_clear_object (&info);
+ return info;
+}
+
+static gboolean
+ostree_repo_file_enumerator_close (GFileEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return TRUE;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_REPO_FILE_ENUMERATOR
+#define _OSTREE_REPO_FILE_ENUMERATOR
+
+#include "ostree-repo-file.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE_ENUMERATOR (_ostree_repo_file_enumerator_get_type ())
+#define OSTREE_REPO_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumerator))
+#define OSTREE_REPO_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_REPO_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+
+typedef struct _OstreeRepoFileEnumerator OstreeRepoFileEnumerator;
+typedef struct _OstreeRepoFileEnumeratorClass OstreeRepoFileEnumeratorClass;
+
+struct _OstreeRepoFileEnumeratorClass
+{
+ GFileEnumeratorClass parent_class;
+};
+
+GType _ostree_repo_file_enumerator_get_type (void) G_GNUC_CONST;
+
+GFileEnumerator * _ostree_repo_file_enumerator_new (OstreeRepoFile *dir,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+
+static void ostree_repo_file_file_iface_init (GFileIface *iface);
+
+static void
+tree_replace_contents (OstreeRepoFile *self,
+ GVariant *new_files,
+ GVariant *new_dirs);
+
+struct _OstreeRepoFile
+{
+ GObject parent_instance;
+
+ OstreeRepo *repo;
+
+ char *commit;
+ GError *commit_resolve_error;
+
+ OstreeRepoFile *parent;
+ int index;
+ char *name;
+
+ char *tree_contents_checksum;
+ GVariant *tree_contents;
+ char *tree_metadata_checksum;
+ GVariant *tree_metadata;
+};
+
+#define ostree_repo_file_get_type _ostree_repo_file_get_type
+G_DEFINE_TYPE_WITH_CODE (OstreeRepoFile, ostree_repo_file, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+ ostree_repo_file_file_iface_init))
+
+static void
+ostree_repo_file_finalize (GObject *object)
+{
+ OstreeRepoFile *self;
+
+ self = OSTREE_REPO_FILE (object);
+
+ if (self->tree_contents)
+ g_variant_unref (self->tree_contents);
+ if (self->tree_metadata)
+ g_variant_unref (self->tree_metadata);
+ g_free (self->tree_contents_checksum);
+ g_free (self->tree_metadata_checksum);
+ g_free (self->commit);
+ g_free (self->name);
+
+ G_OBJECT_CLASS (ostree_repo_file_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_file_dispose (GObject *object)
+{
+ OstreeRepoFile *self;
+
+ self = OSTREE_REPO_FILE (object);
+
+ g_clear_object (&self->repo);
+ g_clear_object (&self->parent);
+
+ if (G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose)
+ G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_class_init (OstreeRepoFileClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = ostree_repo_file_finalize;
+ gobject_class->dispose = ostree_repo_file_dispose;
+}
+
+static void
+ostree_repo_file_init (OstreeRepoFile *self)
+{
+ self->index = -1;
+}
+
+static gboolean
+set_error_noent (GFile *self, GError **error)
+{
+ char *path = g_file_get_path (self);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "No such file or directory: %s", path);
+ g_free (path);
+ return FALSE;
+}
+
+GFile *
+_ostree_repo_file_new_root (OstreeRepo *repo,
+ const char *commit)
+{
+ OstreeRepoFile *self;
+
+ g_return_val_if_fail (repo != NULL, NULL);
+ g_return_val_if_fail (commit != NULL, NULL);
+ g_return_val_if_fail (strlen (commit) == 64, NULL);
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+ self->repo = g_object_ref (repo);
+ self->commit = g_strdup (commit);
+
+ return G_FILE (self);
+}
+
+
+GFile *
+_ostree_repo_file_new_child (OstreeRepoFile *parent,
+ const char *name)
+{
+ OstreeRepoFile *self;
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+ self->repo = g_object_ref (parent->repo);
+ self->parent = g_object_ref (parent);
+ self->name = g_strdup (name);
+
+ return G_FILE (self);
+}
+
+OstreeRepoFile *
+_ostree_repo_file_new_empty_tree (OstreeRepo *repo)
+{
+ OstreeRepoFile *self;
+
+ self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+ self->repo = g_object_ref (repo);
+
+ tree_replace_contents (self, NULL, NULL);
+
+ return self;
+}
+
+static gboolean
+do_resolve_commit (OstreeRepoFile *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *commit = NULL;
+ GVariant *root_contents = NULL;
+ GVariant *root_metadata = NULL;
+ const char *tree_contents_checksum;
+ const char *tree_meta_checksum;
+
+ g_assert (self->parent == NULL);
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ self->commit, &commit, error))
+ goto out;
+
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+ g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+ tree_contents_checksum, &root_contents,
+ error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ tree_meta_checksum, &root_metadata,
+ error))
+ goto out;
+
+ self->tree_metadata = root_metadata;
+ root_metadata = NULL;
+ self->tree_contents = root_contents;
+ root_contents = NULL;
+
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_contents)
+ g_variant_unref (root_contents);
+ return ret;
+}
+
+static gboolean
+do_resolve_nonroot (OstreeRepoFile *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *container = NULL;
+ GVariant *tree_contents = NULL;
+ GVariant *tree_metadata = NULL;
+ gboolean is_dir;
+ int i;
+
+ i = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, &container);
+
+ if (i < 0)
+ {
+ set_error_noent ((GFile*)self, error);
+ goto out;
+ }
+
+ if (is_dir)
+ {
+ const char *name;
+ const char *content_checksum;
+ const char *metadata_checksum;
+ GVariant *files_variant;
+
+ files_variant = g_variant_get_child_value (self->parent->tree_contents, 2);
+ self->index = g_variant_n_children (files_variant) + i;
+ g_variant_unref (files_variant);
+
+ g_variant_get_child (container, i, "(&s&s&s)",
+ &name, &content_checksum, &metadata_checksum);
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+ content_checksum, &tree_contents,
+ error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ metadata_checksum, &tree_metadata,
+ error))
+ goto out;
+
+ self->tree_contents = tree_contents;
+ tree_contents = NULL;
+ self->tree_metadata = tree_metadata;
+ tree_metadata = NULL;
+ }
+ else
+ self->index = i;
+
+ ret = TRUE;
+ out:
+ if (container)
+ g_variant_unref (container);
+ if (tree_metadata)
+ g_variant_unref (tree_metadata);
+ if (tree_contents)
+ g_variant_unref (tree_contents);
+ return ret;
+}
+
+gboolean
+_ostree_repo_file_ensure_resolved (OstreeRepoFile *self,
+ GError **error)
+{
+ if (self->commit_resolve_error != NULL)
+ goto out;
+
+ if (self->parent == NULL)
+ {
+ if (self->tree_contents == NULL)
+ (void)do_resolve_commit (self, &(self->commit_resolve_error));
+ }
+ else if (self->index == -1)
+ {
+ (void)do_resolve_nonroot (self, &(self->commit_resolve_error));
+ }
+
+ out:
+ if (self->commit_resolve_error)
+ {
+ if (error)
+ *error = g_error_copy (self->commit_resolve_error);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+gboolean
+_ostree_repo_file_get_xattrs (OstreeRepoFile *self,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *ret_xattrs = NULL;
+ GVariant *metadata = NULL;
+ GInputStream *input = NULL;
+ GFile *local_file = NULL;
+
+ if (!_ostree_repo_file_ensure_resolved (self, error))
+ goto out;
+
+ if (self->tree_metadata)
+ ret_xattrs = g_variant_get_child_value (self->tree_metadata, 4);
+ else if (ostree_repo_is_archive (self->repo))
+ {
+ local_file = _ostree_repo_file_nontree_get_local (self);
+ if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+ goto out;
+ ret_xattrs = g_variant_get_child_value (metadata, 4);
+ }
+ else
+ {
+ local_file = _ostree_repo_file_nontree_get_local (self);
+ ret_xattrs = ostree_get_xattrs_for_path (ot_gfile_get_path_cached (local_file), error);
+ }
+
+ ret = TRUE;
+ *out_xattrs = ret_xattrs;
+ ret_xattrs = NULL;
+ out:
+ if (ret_xattrs)
+ g_variant_unref (ret_xattrs);
+ if (metadata)
+ g_variant_unref (metadata);
+ g_clear_object (&input);
+ g_clear_object (&local_file);
+ return ret;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_contents (OstreeRepoFile *self)
+{
+ return self->tree_contents;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_metadata (OstreeRepoFile *self)
+{
+ return self->tree_metadata;
+}
+
+void
+_ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+ const char *checksum,
+ GVariant *metadata)
+{
+ if (self->tree_metadata)
+ g_variant_unref (self->tree_metadata);
+ self->tree_metadata = g_variant_ref (metadata);
+ g_free (self->tree_metadata_checksum);
+ self->tree_metadata_checksum = g_strdup (checksum);
+}
+
+void
+_ostree_repo_file_make_empty_tree (OstreeRepoFile *self)
+{
+ tree_replace_contents (self, NULL, NULL);
+}
+
+void
+_ostree_repo_file_tree_set_content_checksum (OstreeRepoFile *self,
+ const char *checksum)
+{
+ g_assert (self->parent == NULL);
+ g_free (self->tree_contents_checksum);
+ self->tree_contents_checksum = g_strdup (checksum);
+}
+
+const char *
+_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile *self)
+{
+ g_assert (self->parent == NULL);
+ return self->tree_contents_checksum;
+}
+
+GFile *
+_ostree_repo_file_nontree_get_local (OstreeRepoFile *self)
+{
+ const char *checksum;
+ char *path;
+ GFile *ret;
+
+ g_assert (!ostree_repo_is_archive (self->repo));
+
+ checksum = _ostree_repo_file_nontree_get_checksum (self);
+ path = ostree_repo_get_object_path (self->repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+ ret = ot_util_new_file_for_path (path);
+ g_free (path);
+
+ return ret;
+}
+
+OstreeRepo *
+_ostree_repo_file_get_repo (OstreeRepoFile *self)
+{
+ return self->repo;
+}
+
+OstreeRepoFile *
+_ostree_repo_file_get_root (OstreeRepoFile *self)
+{
+ OstreeRepoFile *parent = self;
+
+ while (parent->parent)
+ parent = parent->parent;
+ return parent;
+}
+
+const char *
+_ostree_repo_file_nontree_get_checksum (OstreeRepoFile *self)
+{
+ int n;
+ gboolean is_dir;
+
+ g_assert (self->parent);
+
+ n = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, NULL);
+ g_assert (n >= 0 && !is_dir);
+
+ return _ostree_repo_file_tree_get_child_checksum (self->parent, n);
+}
+
+const char *
+_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile *self,
+ int n)
+{
+ GVariant *files_variant;
+ const char *checksum;
+
+ g_assert (self->tree_contents);
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+ g_variant_get_child (files_variant, n, "(@s&s)", NULL, &checksum);
+
+ g_variant_unref (files_variant);
+
+ return checksum;
+}
+
+static gboolean
+ostree_repo_file_is_native (GFile *file)
+{
+ return FALSE;
+}
+
+static gboolean
+ostree_repo_file_has_uri_scheme (GFile *file,
+ const char *uri_scheme)
+{
+ return g_ascii_strcasecmp (uri_scheme, "ostree") == 0;
+}
+
+static char *
+ostree_repo_file_get_uri_scheme (GFile *file)
+{
+ return g_strdup ("ostree");
+}
+
+static char *
+ostree_repo_file_get_basename (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ return g_strdup (self->name);
+}
+
+static char *
+ostree_repo_file_get_path (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ OstreeRepoFile *parent;
+ GString *buf;
+ GSList *parents;
+ GSList *iter;
+
+ buf = g_string_new ("");
+ parents = NULL;
+
+ for (parent = self->parent; parent; parent = parent->parent)
+ parents = g_slist_prepend (parents, parent);
+
+ if (parents->next)
+ {
+ for (iter = parents->next; iter; iter = iter->next)
+ {
+ parent = iter->data;
+ g_string_append_c (buf, '/');
+ g_string_append (buf, parent->name);
+ }
+ }
+ g_string_append_c (buf, '/');
+ g_string_append (buf, self->name);
+
+ g_slist_free (parents);
+
+ return g_string_free (buf, FALSE);
+}
+
+static char *
+ostree_repo_file_get_uri (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ char *path;
+ char *uri_path;
+ char *ret;
+
+ path = g_file_get_path (file);
+ uri_path = g_filename_to_uri (path, NULL, NULL);
+ g_free (path);
+ g_assert (g_str_has_prefix (uri_path, "file://"));
+ ret = g_strconcat ("ostree://", self->commit, uri_path+strlen("file://"), NULL);
+ g_free (uri_path);
+
+ return ret;
+}
+
+static char *
+ostree_repo_file_get_parse_name (GFile *file)
+{
+ return ostree_repo_file_get_uri (file);
+}
+
+static GFile *
+ostree_repo_file_get_parent (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ return g_object_ref (self->parent);
+}
+
+static GFile *
+ostree_repo_file_dup (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ if (self->parent)
+ return _ostree_repo_file_new_child (self->parent, self->name);
+ else
+ return _ostree_repo_file_new_root (self->repo, self->commit);
+}
+
+static guint
+ostree_repo_file_hash (GFile *file)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ if (self->parent)
+ return g_file_hash (self->parent) + g_str_hash (self->name);
+ else
+ return g_str_hash (self->commit);
+}
+
+static gboolean
+ostree_repo_file_equal (GFile *file1,
+ GFile *file2)
+{
+ OstreeRepoFile *self1 = OSTREE_REPO_FILE (file1);
+ OstreeRepoFile *self2 = OSTREE_REPO_FILE (file2);
+
+ if (self1->parent && self2->parent)
+ {
+ return g_str_equal (self1->name, self2->name)
+ && g_file_equal ((GFile*)self1->parent, (GFile*)self2->parent);
+ }
+ else if (!self1->parent && !self2->parent)
+ {
+ return g_str_equal (self1->commit, self2->commit);
+ }
+ else
+ return FALSE;
+}
+
+static const char *
+match_prefix (const char *path,
+ const char *prefix)
+{
+ int prefix_len;
+
+ prefix_len = strlen (prefix);
+ if (strncmp (path, prefix, prefix_len) != 0)
+ return NULL;
+
+ /* Handle the case where prefix is the root, so that
+ * the IS_DIR_SEPRARATOR check below works */
+ if (prefix_len > 0 &&
+ G_IS_DIR_SEPARATOR (prefix[prefix_len-1]))
+ prefix_len--;
+
+ return path + prefix_len;
+}
+
+static gboolean
+ostree_repo_file_prefix_matches (GFile *parent,
+ GFile *descendant)
+{
+ const char *remainder;
+ char *parent_path;
+ char *descendant_path;
+
+ parent_path = g_file_get_path (parent);
+ descendant_path = g_file_get_path (descendant);
+ remainder = match_prefix (descendant_path, parent_path);
+ g_free (parent_path);
+ g_free (descendant_path);
+ if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+ return TRUE;
+ return FALSE;
+}
+
+static char *
+ostree_repo_file_get_relative_path (GFile *parent,
+ GFile *descendant)
+{
+ const char *remainder;
+ char *parent_path;
+ char *descendant_path;
+
+ parent_path = g_file_get_path (parent);
+ descendant_path = g_file_get_path (descendant);
+ remainder = match_prefix (descendant_path, parent_path);
+ g_free (parent_path);
+ g_free (descendant_path);
+
+ if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+ return g_strdup (remainder + 1);
+ return NULL;
+}
+
+static GFile *
+ostree_repo_file_resolve_relative_path (GFile *file,
+ const char *relative_path)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ OstreeRepoFile *parent;
+ char *filename;
+ const char *rest;
+ GFile *ret;
+
+ if (g_path_is_absolute (relative_path) && self->parent)
+ {
+ g_assert (*relative_path == '/');
+ return ostree_repo_file_resolve_relative_path ((GFile*)_ostree_repo_file_get_root (self),
+ relative_path+1);
+ }
+
+ rest = strchr (relative_path, '/');
+ if (rest)
+ {
+ rest += 1;
+ filename = g_strndup (relative_path, rest - relative_path);
+ }
+ else
+ filename = g_strdup (relative_path);
+
+ parent = (OstreeRepoFile*)_ostree_repo_file_new_child (self, filename);
+ g_free (filename);
+
+ if (!rest)
+ ret = (GFile*)parent;
+ else
+ {
+ ret = ostree_repo_file_resolve_relative_path ((GFile*)parent, rest);
+ g_clear_object (&parent);
+ }
+ return ret;
+}
+
+static GFileEnumerator *
+ostree_repo_file_enumerate_children (GFile *file,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ return _ostree_repo_file_enumerator_new (self,
+ attributes, flags,
+ cancellable, error);
+}
+
+static GFile *
+ostree_repo_file_get_child_for_display_name (GFile *file,
+ const char *display_name,
+ GError **error)
+{
+ return g_file_get_child (file, display_name);
+}
+
+static GFile *
+get_child_local_file (OstreeRepo *repo,
+ const char *checksum)
+{
+ char *path;
+ GFile *ret;
+
+ path = ostree_repo_get_object_path (repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+ ret = ot_util_new_file_for_path (path);
+ g_free (path);
+
+ return ret;
+}
+
+static gboolean
+query_child_info_file_nonarchive (OstreeRepo *repo,
+ const char *checksum,
+ GFileAttributeMatcher *matcher,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFileInfo *local_info = NULL;
+ GFile *local_file = NULL;
+ int i ;
+ const char *mapped_boolean[] = {
+ "standard::is-symlink"
+ };
+ const char *mapped_string[] = {
+ };
+ const char *mapped_byte_string[] = {
+ "standard::symlink-target"
+ };
+ const char *mapped_uint32[] = {
+ "standard::type",
+ "unix::device",
+ "unix::mode",
+ "unix::nlink",
+ "unix::uid",
+ "unix::gid",
+ "unix::rdev"
+ };
+ const char *mapped_uint64[] = {
+ "standard::size",
+ "standard::allocated-size",
+ "unix::inode"
+ };
+
+ if (!(g_file_attribute_matcher_matches (matcher, "unix::mode")
+ || g_file_attribute_matcher_matches (matcher, "standard::type")))
+ {
+ ret = TRUE;
+ goto out;
+ }
+
+ local_file = get_child_local_file (repo, checksum);
+ local_info = g_file_query_info (local_file,
+ OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable,
+ error);
+ if (!local_info)
+ goto out;
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_boolean); i++)
+ g_file_info_set_attribute_boolean (info, mapped_boolean[i], g_file_info_get_attribute_boolean (local_info, mapped_boolean[i]));
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_string); i++)
+ {
+ const char *string = g_file_info_get_attribute_string (local_info, mapped_string[i]);
+ if (string)
+ g_file_info_set_attribute_string (info, mapped_string[i], string);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_byte_string); i++)
+ {
+ const char *byte_string = g_file_info_get_attribute_byte_string (local_info, mapped_byte_string[i]);
+ if (byte_string)
+ g_file_info_set_attribute_byte_string (info, mapped_byte_string[i], byte_string);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_uint32); i++)
+ g_file_info_set_attribute_uint32 (info, mapped_uint32[i], g_file_info_get_attribute_uint32 (local_info, mapped_uint32[i]));
+
+ for (i = 0; i < G_N_ELEMENTS (mapped_uint64); i++)
+ g_file_info_set_attribute_uint64 (info, mapped_uint64[i], g_file_info_get_attribute_uint64 (local_info, mapped_uint64[i]));
+
+ ret = TRUE;
+ out:
+ g_clear_object (&local_info);
+ g_clear_object (&local_file);
+ return ret;
+}
+
+static gboolean
+query_child_info_file_archive (OstreeRepo *repo,
+ const char *checksum,
+ GFileAttributeMatcher *matcher,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *local_file = NULL;
+ GVariant *metadata = NULL;
+ GInputStream *input = NULL;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ guint32 file_type;
+ gsize bytes_read;
+ char *buf = NULL;
+
+ local_file = get_child_local_file (repo, checksum);
+
+ if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+ goto out;
+
+ g_variant_get (metadata, "(uuuu@a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ NULL, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ g_file_info_set_attribute_boolean (info, "standard::is-symlink",
+ S_ISLNK (mode));
+ if (S_ISLNK (mode))
+ file_type = G_FILE_TYPE_SYMBOLIC_LINK;
+ else if (S_ISREG (mode))
+ file_type = G_FILE_TYPE_REGULAR;
+ else if (S_ISBLK (mode) || S_ISCHR(mode) || S_ISFIFO(mode))
+ file_type = G_FILE_TYPE_SPECIAL;
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile %s: Invalid mode", checksum);
+ goto out;
+ }
+ g_file_info_set_attribute_uint32 (info, "standard::type", file_type);
+
+ g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+ g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+ g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+
+ if (file_type == G_FILE_TYPE_REGULAR)
+ {
+ g_file_info_set_attribute_uint64 (info, "standard::size", content_len);
+ }
+ else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
+ {
+ gsize len = MIN (PATH_MAX, content_len) + 1;
+ buf = g_malloc (len);
+
+ if (!g_input_stream_read_all (input, buf, len, &bytes_read, cancellable, error))
+ goto out;
+ buf[bytes_read] = '\0';
+
+ g_file_info_set_attribute_byte_string (info, "standard::symlink-target", buf);
+ }
+ else if (file_type == G_FILE_TYPE_SPECIAL)
+ {
+ guint32 device;
+
+ if (!g_input_stream_read_all (input, &device, 4, &bytes_read, cancellable, error))
+ goto out;
+
+ device = GUINT32_FROM_BE (device);
+ g_file_info_set_attribute_uint32 (info, "unix::device", device);
+ }
+
+ ret = TRUE;
+ out:
+ g_free (buf);
+ if (metadata)
+ g_variant_unref (metadata);
+ g_clear_object (&local_file);
+ g_clear_object (&input);
+ return ret;
+}
+
+static void
+set_info_from_dirmeta (GFileInfo *info,
+ GVariant *metadata)
+{
+ guint32 version, uid, gid, mode;
+
+ g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_DIRECTORY);
+
+ /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
+ g_variant_get (metadata, "(uuuu@a(ayay))",
+ &version, &uid, &gid, &mode,
+ NULL);
+ version = GUINT32_FROM_BE (version);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+
+ g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+ g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+ g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+}
+
+static gboolean
+query_child_info_dir (OstreeRepo *repo,
+ const char *metadata_checksum,
+ GFileAttributeMatcher *matcher,
+ GFileQueryInfoFlags flags,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *metadata = NULL;
+
+ if (!g_file_attribute_matcher_matches (matcher, "unix::mode"))
+ {
+ ret = TRUE;
+ goto out;
+ }
+
+ if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ metadata_checksum, &metadata, error))
+ goto out;
+
+ set_info_from_dirmeta (info, metadata);
+
+ ret = TRUE;
+ out:
+ if (metadata)
+ g_variant_unref (metadata);
+ return ret;
+}
+
+static gboolean
+bsearch_in_file_variant (GVariant *variant,
+ const char *name,
+ int *out_pos)
+{
+ int i, n;
+ int m;
+
+ i = 0;
+ n = g_variant_n_children (variant) - 1;
+ m = 0;
+
+ while (i <= n)
+ {
+ GVariant *child;
+ const char *cur;
+ int cmp;
+
+ m = i + ((n - i) / 2);
+
+ child = g_variant_get_child_value (variant, m);
+ g_variant_get_child (child, 0, "&s", &cur, NULL);
+
+ cmp = strcmp (cur, name);
+ if (cmp < 0)
+ i = m + 1;
+ else if (cmp > 0)
+ n = m - 1;
+ else
+ {
+ g_variant_unref (child);
+ *out_pos = m;
+ return TRUE;
+ }
+ g_variant_unref (child);
+ }
+
+ *out_pos = m;
+ return FALSE;
+}
+
+static GVariant *
+remove_variant_child (GVariant *variant,
+ int n)
+{
+ GVariantBuilder builder;
+ GVariantIter *iter;
+ int i;
+ GVariant *child;
+
+ g_variant_builder_init (&builder, g_variant_get_type (variant));
+ iter = g_variant_iter_new (variant);
+
+ i = 0;
+ while ((child = g_variant_iter_next_value (iter)) != NULL)
+ {
+ if (i != n)
+ g_variant_builder_add_value (&builder, child);
+ g_variant_unref (child);
+ }
+ g_variant_iter_free (iter);
+
+ return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+insert_variant_child (GVariant *variant,
+ int n,
+ GVariant *item)
+{
+ GVariantBuilder builder;
+ GVariantIter *iter;
+ int i;
+ GVariant *child;
+
+ g_variant_builder_init (&builder, g_variant_get_type (variant));
+ iter = g_variant_iter_new (variant);
+
+ i = 0;
+ while ((child = g_variant_iter_next_value (iter)) != NULL)
+ {
+ if (i == n)
+ g_variant_builder_add_value (&builder, item);
+ g_variant_builder_add_value (&builder, child);
+ g_variant_unref (child);
+ }
+ g_variant_iter_free (iter);
+
+ return g_variant_builder_end (&builder);
+}
+
+static void
+tree_replace_contents (OstreeRepoFile *self,
+ GVariant *new_files,
+ GVariant *new_dirs)
+{
+ guint version;
+ GVariant *metadata;
+ GVariant *tmp_files = NULL;
+ GVariant *tmp_dirs = NULL;
+
+ if (!(new_files || new_dirs) && self->tree_contents)
+ return;
+ else if (!self->tree_contents)
+ {
+ version = GUINT32_TO_BE (0);
+ metadata = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+ tmp_dirs = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+ tmp_files = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+ }
+ else
+ {
+ g_variant_get_child (self->tree_contents, 0, "u", &version);
+ metadata = g_variant_get_child_value (self->tree_contents, 1);
+ if (!new_files)
+ tmp_files = g_variant_get_child_value (self->tree_contents, 2);
+ if (!new_dirs)
+ tmp_dirs = g_variant_get_child_value (self->tree_contents, 3);
+ }
+
+ if (self->tree_contents)
+ g_variant_unref (self->tree_contents);
+ self->tree_contents = g_variant_new ("(u@a{sv}@a(ss)@a(sss))", version, metadata,
+ new_files ? new_files : tmp_files,
+ new_dirs ? new_dirs : tmp_dirs);
+
+ g_variant_unref (metadata);
+ if (tmp_files)
+ g_variant_unref (tmp_files);
+ if (tmp_dirs)
+ g_variant_unref (tmp_dirs);
+}
+
+void
+_ostree_repo_file_tree_remove_child (OstreeRepoFile *self,
+ const char *name)
+{
+ int i;
+ GVariant *files_variant;
+ GVariant *new_files_variant = NULL;
+ GVariant *dirs_variant;
+ GVariant *new_dirs_variant = NULL;
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ if (bsearch_in_file_variant (files_variant, name, &i))
+ {
+ new_files_variant = remove_variant_child (files_variant, i);
+ }
+ else
+ {
+ if (bsearch_in_file_variant (dirs_variant, name, &i))
+ {
+ new_dirs_variant = remove_variant_child (dirs_variant, i);
+ }
+ }
+
+ tree_replace_contents (self, new_files_variant, new_dirs_variant);
+
+ g_variant_unref (files_variant);
+ g_variant_unref (dirs_variant);
+}
+
+void
+_ostree_repo_file_tree_add_file (OstreeRepoFile *self,
+ const char *name,
+ const char *checksum)
+{
+ int n;
+ GVariant *files_variant;
+ GVariant *new_files_variant;
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+ if (!bsearch_in_file_variant (files_variant, name, &n))
+ {
+ new_files_variant = insert_variant_child (files_variant, n,
+ g_variant_new ("(ss)", name, checksum));
+ g_variant_ref_sink (new_files_variant);
+ tree_replace_contents (self, new_files_variant, NULL);
+ g_variant_unref (new_files_variant);
+ }
+ g_variant_unref (files_variant);
+}
+
+void
+_ostree_repo_file_tree_add_dir (OstreeRepoFile *self,
+ const char *name,
+ const char *content_checksum,
+ const char *metadata_checksum)
+{
+ int n;
+ GVariant *dirs_variant;
+ GVariant *new_dirs_variant;
+
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ if (!bsearch_in_file_variant (dirs_variant, name, &n))
+ {
+ new_dirs_variant = insert_variant_child (dirs_variant, n,
+ g_variant_new ("(sss)", name, content_checksum,
+ metadata_checksum));
+ g_variant_ref_sink (new_dirs_variant);
+ tree_replace_contents (self, NULL, new_dirs_variant);
+ g_variant_unref (new_dirs_variant);
+ }
+ g_variant_unref (dirs_variant);
+}
+
+int
+_ostree_repo_file_tree_find_child (OstreeRepoFile *self,
+ const char *name,
+ gboolean *is_dir,
+ GVariant **out_container)
+{
+ int i;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ GVariant *ret_container = NULL;
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ i = -1;
+ if (bsearch_in_file_variant (files_variant, name, &i))
+ {
+ *is_dir = FALSE;
+ ret_container = files_variant;
+ files_variant = NULL;
+ }
+ else
+ {
+ if (bsearch_in_file_variant (dirs_variant, name, &i))
+ {
+ *is_dir = TRUE;
+ ret_container = dirs_variant;
+ dirs_variant = NULL;
+ }
+ else
+ {
+ i = -1;
+ }
+ }
+ if (ret_container && out_container)
+ {
+ *out_container = ret_container;
+ ret_container = NULL;
+ }
+ if (ret_container)
+ g_variant_unref (ret_container);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return i;
+}
+
+gboolean
+_ostree_repo_file_tree_query_child (OstreeRepoFile *self,
+ int n,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GFileInfo **out_info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const char *name = NULL;
+ gboolean ret = FALSE;
+ GFileInfo *ret_info = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ GVariant *tree_child_metadata = NULL;
+ GFileAttributeMatcher *matcher = NULL;
+ int c;
+
+ if (!_ostree_repo_file_ensure_resolved (self, error))
+ goto out;
+
+ matcher = g_file_attribute_matcher_new (attributes);
+
+ ret_info = g_file_info_new ();
+
+ g_assert (self->tree_contents);
+
+ files_variant = g_variant_get_child_value (self->tree_contents, 2);
+ dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+ c = g_variant_n_children (files_variant);
+ if (n < c)
+ {
+ const char *checksum;
+
+ g_variant_get_child (files_variant, n, "(&s&s)", &name, &checksum);
+
+ if (ostree_repo_is_archive (self->repo))
+ {
+ if (!query_child_info_file_archive (self->repo, checksum, matcher, ret_info,
+ cancellable, error))
+ goto out;
+ }
+ else
+ {
+ if (!query_child_info_file_nonarchive (self->repo, checksum, matcher, ret_info,
+ cancellable, error))
+ goto out;
+ }
+ }
+ else
+ {
+ const char *tree_checksum;
+ const char *meta_checksum;
+
+ n -= c;
+
+ c = g_variant_n_children (dirs_variant);
+
+ if (n < c)
+ {
+ g_variant_get_child (dirs_variant, n, "(&s&s&s)",
+ &name, &tree_checksum, &meta_checksum);
+
+ if (!query_child_info_dir (self->repo, meta_checksum,
+ matcher, flags, ret_info,
+ cancellable, error))
+ goto out;
+ }
+ else
+ n -= c;
+ }
+
+ if (name)
+ {
+ g_file_info_set_attribute_byte_string (ret_info, "standard::name",
+ name);
+ g_file_info_set_attribute_string (ret_info, "standard::display-name",
+ name);
+ if (*name == '.')
+ g_file_info_set_is_hidden (ret_info, TRUE);
+ }
+ else
+ {
+ g_clear_object (&ret_info);
+ }
+
+ ret = TRUE;
+ *out_info = ret_info;
+ ret_info = NULL;
+ out:
+ g_clear_object (&ret_info);
+ if (matcher)
+ g_file_attribute_matcher_unref (matcher);
+ if (tree_child_metadata)
+ g_variant_unref (tree_child_metadata);
+ g_variant_unref (files_variant);
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static GFileInfo *
+ostree_repo_file_query_info (GFile *file,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+ gboolean ret = FALSE;
+ GFileInfo *info = NULL;
+
+ if (!_ostree_repo_file_ensure_resolved (self, error))
+ goto out;
+
+ if (!self->parent)
+ {
+ info = g_file_info_new ();
+ set_info_from_dirmeta (info, self->tree_metadata);
+ }
+ else
+ {
+ if (!_ostree_repo_file_tree_query_child (self->parent, self->index,
+ attributes, flags,
+ &info, cancellable, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ g_clear_object (&info);
+ return info;
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_settable_attributes (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_file_attribute_info_list_new ();
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_writable_namespaces (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_file_attribute_info_list_new ();
+}
+
+static GFileInputStream *
+ostree_repo_file_read (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *local_file = NULL;
+ GFileInputStream *ret_stream = NULL;
+ OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+ if (self->tree_contents)
+ {
+ g_set_error_literal (error, G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ "Can't open directory");
+ goto out;
+ }
+
+ if (ostree_repo_is_archive (self->repo))
+ {
+ g_set_error_literal (error, G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Can't open archived file (yet)");
+ goto out;
+ }
+ else
+ {
+ local_file = _ostree_repo_file_nontree_get_local (self);
+ ret_stream = g_file_read (local_file, cancellable, error);
+ if (!ret_stream)
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&local_file);
+ if (!ret)
+ g_clear_object (&ret_stream);
+ return ret_stream;
+}
+
+static void
+ostree_repo_file_file_iface_init (GFileIface *iface)
+{
+ iface->dup = ostree_repo_file_dup;
+ iface->hash = ostree_repo_file_hash;
+ iface->equal = ostree_repo_file_equal;
+ iface->is_native = ostree_repo_file_is_native;
+ iface->has_uri_scheme = ostree_repo_file_has_uri_scheme;
+ iface->get_uri_scheme = ostree_repo_file_get_uri_scheme;
+ iface->get_basename = ostree_repo_file_get_basename;
+ iface->get_path = ostree_repo_file_get_path;
+ iface->get_uri = ostree_repo_file_get_uri;
+ iface->get_parse_name = ostree_repo_file_get_parse_name;
+ iface->get_parent = ostree_repo_file_get_parent;
+ iface->prefix_matches = ostree_repo_file_prefix_matches;
+ iface->get_relative_path = ostree_repo_file_get_relative_path;
+ iface->resolve_relative_path = ostree_repo_file_resolve_relative_path;
+ iface->get_child_for_display_name = ostree_repo_file_get_child_for_display_name;
+ iface->set_display_name = NULL;
+ iface->enumerate_children = ostree_repo_file_enumerate_children;
+ iface->query_info = ostree_repo_file_query_info;
+ iface->query_filesystem_info = NULL;
+ iface->find_enclosing_mount = NULL;
+ iface->query_settable_attributes = ostree_repo_file_query_settable_attributes;
+ iface->query_writable_namespaces = ostree_repo_file_query_writable_namespaces;
+ iface->set_attribute = NULL;
+ iface->set_attributes_from_info = NULL;
+ iface->read_fn = ostree_repo_file_read;
+ iface->append_to = NULL;
+ iface->create = NULL;
+ iface->replace = NULL;
+ iface->open_readwrite = NULL;
+ iface->create_readwrite = NULL;
+ iface->replace_readwrite = NULL;
+ iface->delete_file = NULL;
+ iface->trash = NULL;
+ iface->make_directory = NULL;
+ iface->make_symbolic_link = NULL;
+ iface->copy = NULL;
+ iface->move = NULL;
+ iface->monitor_dir = NULL;
+ iface->monitor_file = NULL;
+
+ iface->supports_thread_contexts = TRUE;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_REPO_FILE
+#define _OSTREE_REPO_FILE
+
+#include "ostree-repo.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE (_ostree_repo_file_get_type ())
+#define OSTREE_REPO_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFile))
+#define OSTREE_REPO_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+#define OSTREE_IS_LOCAL_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_IS_LOCAL_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_REPO_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+
+typedef struct _OstreeRepoFile OstreeRepoFile;
+typedef struct _OstreeRepoFileClass OstreeRepoFileClass;
+
+struct _OstreeRepoFileClass
+{
+ GObjectClass parent_class;
+};
+
+GType _ostree_repo_file_get_type (void) G_GNUC_CONST;
+
+GFile * _ostree_repo_file_new_root (OstreeRepo *repo,
+ const char *commit);
+
+OstreeRepoFile * _ostree_repo_file_new_empty_tree (OstreeRepo *repo);
+
+GFile * _ostree_repo_file_new_child (OstreeRepoFile *parent,
+ const char *name);
+
+gboolean _ostree_repo_file_ensure_resolved (OstreeRepoFile *self,
+ GError **error);
+
+gboolean _ostree_repo_file_get_xattrs (OstreeRepoFile *self,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error);
+
+OstreeRepo * _ostree_repo_file_get_repo (OstreeRepoFile *self);
+OstreeRepoFile * _ostree_repo_file_get_root (OstreeRepoFile *self);
+
+void _ostree_repo_file_make_empty_tree (OstreeRepoFile *self);
+
+void _ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+ const char *checksum,
+ GVariant *metadata);
+
+void _ostree_repo_file_tree_set_content_checksum (OstreeRepoFile *self,
+ const char *checksum);
+const char *_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile *self);
+
+gboolean _ostree_repo_file_is_tree (OstreeRepoFile *self);
+
+const char * _ostree_repo_file_nontree_get_checksum (OstreeRepoFile *self);
+
+GFile *_ostree_repo_file_nontree_get_local (OstreeRepoFile *self);
+
+void _ostree_repo_file_tree_remove_child (OstreeRepoFile *self,
+ const char *name);
+
+void _ostree_repo_file_tree_add_file (OstreeRepoFile *self,
+ const char *name,
+ const char *checksum);
+
+void _ostree_repo_file_tree_add_dir (OstreeRepoFile *self,
+ const char *name,
+ const char *content_checksum,
+ const char *metadata_checksum);
+
+int _ostree_repo_file_tree_find_child (OstreeRepoFile *self,
+ const char *name,
+ gboolean *is_dir,
+ GVariant **out_container);
+
+const char *_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile *self,
+ int n);
+
+gboolean _ostree_repo_file_tree_query_child (OstreeRepoFile *self,
+ int n,
+ const char *attributes,
+ GFileQueryInfoFlags flags,
+ GFileInfo **out_info,
+ GCancellable *cancellable,
+ GError **error);
+
+GVariant *_ostree_repo_file_tree_get_contents (OstreeRepoFile *self);
+GVariant *_ostree_repo_file_tree_get_metadata (OstreeRepoFile *self);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+#include "ostree-repo-file-enumerator.h"
+
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+
+enum {
+ PROP_0,
+
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
+
+typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
+
+struct _OstreeRepoPrivate {
+ char *path;
+ GFile *repo_file;
+ GFile *tmp_dir;
+ GFile *local_heads_dir;
+ GFile *remote_heads_dir;
+ char *objects_path;
+ char *config_path;
+
+ gboolean inited;
+
+ GKeyFile *config;
+ gboolean archive;
+};
+
+static void
+ostree_repo_finalize (GObject *object)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->path);
+ g_clear_object (&priv->repo_file);
+ g_clear_object (&priv->tmp_dir);
+ g_clear_object (&priv->local_heads_dir);
+ g_clear_object (&priv->remote_heads_dir);
+ g_free (priv->objects_path);
+ g_free (priv->config_path);
+ if (priv->config)
+ g_key_file_free (priv->config);
+
+ G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ priv->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_repo_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ostree_repo_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+ OstreeRepoPrivate *priv;
+
+ parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ priv = GET_PRIVATE (object);
+
+ g_assert (priv->path != NULL);
+
+ priv->repo_file = ot_util_new_file_for_path (priv->path);
+ priv->tmp_dir = g_file_resolve_relative_path (priv->repo_file, "tmp");
+ priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
+ priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
+
+ priv->objects_path = g_build_filename (priv->path, "objects", NULL);
+ priv->config_path = g_build_filename (priv->path, "config", NULL);
+
+ return object;
+}
+
+static void
+ostree_repo_class_init (OstreeRepoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
+
+ object_class->constructor = ostree_repo_constructor;
+ object_class->get_property = ostree_repo_get_property;
+ object_class->set_property = ostree_repo_set_property;
+ object_class->finalize = ostree_repo_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path",
+ "",
+ "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_repo_init (OstreeRepo *self)
+{
+}
+
+OstreeRepo*
+ostree_repo_new (const char *path)
+{
+ return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
+}
+
+static gboolean
+parse_rev_file (OstreeRepo *self,
+ const char *path,
+ char **sha256,
+ GError **error) G_GNUC_UNUSED;
+
+static gboolean
+parse_rev_file (OstreeRepo *self,
+ const char *path,
+ char **sha256,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GError *temp_error = NULL;
+ gboolean ret = FALSE;
+ char *rev = NULL;
+
+ rev = ot_util_get_file_contents_utf8 (path, &temp_error);
+ if (rev == NULL)
+ {
+ if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+ else
+ {
+ g_strchomp (rev);
+ }
+
+ if (g_str_has_prefix (rev, "ref: "))
+ {
+ GFile *ref;
+ char *ref_path;
+ char *ref_sha256;
+ gboolean subret;
+
+ ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
+ ref_path = g_file_get_path (ref);
+
+ subret = parse_rev_file (self, ref_path, &ref_sha256, error);
+ g_clear_object (&ref);
+ g_free (ref_path);
+
+ if (!subret)
+ {
+ g_free (ref_sha256);
+ goto out;
+ }
+
+ g_free (rev);
+ rev = ref_sha256;
+ }
+ else
+ {
+ if (!ostree_validate_checksum_string (rev, error))
+ goto out;
+ }
+
+ *sha256 = rev;
+ rev = NULL;
+ ret = TRUE;
+ out:
+ g_free (rev);
+ return ret;
+}
+
+static gboolean
+resolve_rev (OstreeRepo *self,
+ const char *rev,
+ gboolean allow_noent,
+ char **sha256,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *tmp = NULL;
+ char *tmp2 = NULL;
+ char *ret_rev = NULL;
+ GFile *child = NULL;
+ char *child_path = NULL;
+ GError *temp_error = NULL;
+ GVariant *commit = NULL;
+
+ if (strlen (rev) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty rev");
+ goto out;
+ }
+ else if (strlen (rev) == 64)
+ {
+ ret_rev = g_strdup (rev);
+ }
+ else if (g_str_has_suffix (rev, "^"))
+ {
+ tmp = g_strdup (rev);
+ tmp[strlen(tmp) - 1] = '\0';
+
+ if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
+ goto out;
+
+ g_variant_get_child (commit, 2, "s", &ret_rev);
+ if (strlen (ret_rev) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Commit %s has no parent", tmp2);
+ goto out;
+
+ }
+ }
+ else
+ {
+ child = g_file_get_child (priv->local_heads_dir, rev);
+ child_path = g_file_get_path (child);
+ if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
+ {
+ if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_free (ret_rev);
+ ret_rev = NULL;
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
+ goto out;
+ }
+ }
+ else
+ {
+ g_strchomp (ret_rev);
+
+ if (!ostree_validate_checksum_string (ret_rev, error))
+ goto out;
+ }
+ }
+
+ *sha256 = ret_rev;
+ ret_rev = NULL;
+ ret = TRUE;
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ g_free (tmp);
+ g_free (tmp2);
+ g_clear_object (&child);
+ g_free (child_path);
+ g_free (ret_rev);
+ return ret;
+}
+
+gboolean
+ostree_repo_resolve_rev (OstreeRepo *self,
+ const char *rev,
+ char **sha256,
+ GError **error)
+{
+ g_return_val_if_fail (rev != NULL, FALSE);
+ return resolve_rev (self, rev, FALSE, sha256, error);
+}
+
+static gboolean
+write_checksum_file (GFile *parentdir,
+ const char *name,
+ const char *sha256,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *child = NULL;
+ GOutputStream *out = NULL;
+ gsize bytes_written;
+
+ child = g_file_get_child (parentdir, name);
+
+ if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
+ goto out;
+ if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_close (out, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&child);
+ g_clear_object (&out);
+ return ret;
+}
+
+/**
+ * ostree_repo_get_config:
+ * @self:
+ *
+ * Returns: (transfer none): The repository configuration; do not modify
+ */
+GKeyFile *
+ostree_repo_get_config (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->inited, NULL);
+
+ return priv->config;
+}
+
+/**
+ * ostree_repo_copy_config:
+ * @self:
+ *
+ * Returns: (transfer full): A newly-allocated copy of the repository config
+ */
+GKeyFile *
+ostree_repo_copy_config (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GKeyFile *copy;
+ char *data;
+ gsize len;
+
+ g_return_val_if_fail (priv->inited, NULL);
+
+ copy = g_key_file_new ();
+ data = g_key_file_to_data (priv->config, &len, NULL);
+ if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
+ g_assert_not_reached ();
+ g_free (data);
+ return copy;
+}
+
+/**
+ * ostree_repo_write_config:
+ * @self:
+ * @new_config: Overwrite the config file with this data. Do not change later!
+ * @error: a #GError
+ *
+ * Save @new_config in place of this repository's config file. Note
+ * that @new_config should not be modified after - this function
+ * simply adds a reference.
+ */
+gboolean
+ostree_repo_write_config (OstreeRepo *self,
+ GKeyFile *new_config,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *data = NULL;
+ gsize len;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ data = g_key_file_to_data (new_config, &len, error);
+ if (!g_file_set_contents (priv->config_path, data, len, error))
+ goto out;
+
+ g_key_file_free (priv->config);
+ priv->config = g_key_file_new ();
+ if (!g_key_file_load_from_data (priv->config, data, len, 0, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (data);
+ return ret;
+}
+
+gboolean
+ostree_repo_check (OstreeRepo *self, GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *version = NULL;;
+ GError *temp_error = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (priv->inited)
+ return TRUE;
+
+ if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Couldn't find objects directory '%s'", priv->objects_path);
+ goto out;
+ }
+
+ priv->config = g_key_file_new ();
+ if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
+ {
+ g_prefix_error (error, "Couldn't parse config file: ");
+ goto out;
+ }
+
+ version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
+ if (temp_error)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ if (strcmp (version, "0") != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid repository version '%s'", version);
+ goto out;
+ }
+
+ priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
+ if (temp_error)
+ {
+ if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+
+ priv->inited = TRUE;
+
+ ret = TRUE;
+ out:
+ g_free (version);
+ return ret;
+}
+
+const char *
+ostree_repo_get_path (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ return priv->path;
+}
+
+gboolean
+ostree_repo_is_archive (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ return priv->archive;
+}
+
+static gboolean
+write_gvariant_to_tmp (OstreeRepo *self,
+ OstreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GVariant *serialized = NULL;
+ gboolean ret = FALSE;
+ gsize bytes_written;
+ char *tmp_name = NULL;
+ char *dest_name = NULL;
+ int fd = -1;
+ GUnixOutputStream *stream = NULL;
+ GChecksum *checksum = NULL;
+
+ serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
+
+ tmp_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir), "variant-tmp-XXXXXX", NULL);
+ fd = g_mkstemp (tmp_name);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
+ if (!g_output_stream_write_all ((GOutputStream*)stream,
+ g_variant_get_data (serialized),
+ g_variant_get_size (serialized),
+ &bytes_written,
+ NULL,
+ error))
+ goto out;
+
+ g_checksum_update (checksum, (guint8*)g_variant_get_data (serialized), g_variant_get_size (serialized));
+
+ if (!g_output_stream_close ((GOutputStream*)stream,
+ NULL, error))
+ goto out;
+
+ dest_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir), g_checksum_get_string (checksum), NULL);
+ if (rename (tmp_name, dest_name) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_checksum = checksum;
+ checksum = NULL;
+ out:
+ /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
+ (void) unlink (tmp_name);
+ if (fd != -1)
+ close (fd);
+ if (checksum)
+ g_checksum_free (checksum);
+ if (serialized != NULL)
+ g_variant_unref (serialized);
+ g_free (tmp_name);
+ g_free (dest_name);
+ g_clear_object (&stream);
+ return ret;
+}
+
+static gboolean
+import_gvariant_object (OstreeRepo *self,
+ OstreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *tmp_name = NULL;
+ GChecksum *ret_checksum = NULL;
+ gboolean did_exist;
+
+ if (!write_gvariant_to_tmp (self, type, variant, &ret_checksum, error))
+ goto out;
+
+ tmp_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir),
+ g_checksum_get_string (ret_checksum), NULL);
+
+ if (!ostree_repo_store_object_trusted (self, tmp_name,
+ g_checksum_get_string (ret_checksum),
+ OSTREE_OBJECT_TYPE_META,
+ TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ ret = TRUE;
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ (void) unlink (tmp_name);
+ g_free (tmp_name);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ return ret;
+}
+
+gboolean
+ostree_repo_load_variant_checked (OstreeRepo *self,
+ OstreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeSerializedVariantType type;
+ GVariant *ret_variant = NULL;
+
+ if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
+ goto out;
+
+ if (type != expected_type)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
+ type, (guint32)expected_type);
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ return ret;
+}
+
+static gboolean
+import_directory_meta (OstreeRepo *self,
+ const char *path,
+ GVariant **out_variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat stbuf;
+ GChecksum *ret_checksum = NULL;
+ GVariant *dirmeta = NULL;
+ GVariant *xattrs = NULL;
+
+ if (lstat (path, &stbuf) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (!S_ISDIR(stbuf.st_mode))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Not a directory: '%s'", path);
+ goto out;
+ }
+
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+
+ dirmeta = g_variant_new ("(uuuu@a(ayay))",
+ OSTREE_DIR_META_VERSION,
+ GUINT32_TO_BE ((guint32)stbuf.st_uid),
+ GUINT32_TO_BE ((guint32)stbuf.st_gid),
+ GUINT32_TO_BE ((guint32)stbuf.st_mode),
+ xattrs);
+ g_variant_ref_sink (dirmeta);
+
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ dirmeta, &ret_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ {
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ if (dirmeta != NULL)
+ g_variant_unref (dirmeta);
+ }
+ else
+ {
+ *out_checksum = ret_checksum;
+ *out_variant = dirmeta;
+ }
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+char *
+ostree_repo_get_object_path (OstreeRepo *self,
+ const char *checksum,
+ OstreeObjectType type)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *ret;
+ char *relpath;
+
+ relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
+ ret = g_build_filename (priv->path, relpath, NULL);
+ g_free (relpath);
+
+ return ret;
+}
+
+static char *
+prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
+ const char *checksum,
+ OstreeObjectType type,
+ GError **error)
+{
+ char *checksum_dir = NULL;
+ char *object_path = NULL;
+
+ object_path = ostree_repo_get_object_path (self, checksum, type);
+ checksum_dir = g_path_get_dirname (object_path);
+
+ if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
+ goto out;
+
+ out:
+ g_free (checksum_dir);
+ return object_path;
+}
+
+static gboolean
+link_object_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ char *src_basename = NULL;
+ char *src_dirname = NULL;
+ char *dest_basename = NULL;
+ char *tmp_dest_basename = NULL;
+ char *dest_dirname = NULL;
+ DIR *src_dir = NULL;
+ DIR *dest_dir = NULL;
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+
+ src_basename = g_path_get_basename (path);
+ src_dirname = g_path_get_dirname (path);
+
+ src_dir = opendir (src_dirname);
+ if (src_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+ if (!dest_path)
+ goto out;
+
+ dest_basename = g_path_get_basename (dest_path);
+ dest_dirname = g_path_get_dirname (dest_path);
+ dest_dir = opendir (dest_dirname);
+ if (dest_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (force)
+ {
+ tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
+ (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+ }
+ else
+ tmp_dest_basename = g_strdup (dest_basename);
+
+ if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
+ {
+ if (errno != EEXIST || !ignore_exists)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ else
+ *did_exist = TRUE;
+ }
+ else
+ *did_exist = FALSE;
+
+ if (force)
+ {
+ if (renameat (dirfd (dest_dir), tmp_dest_basename,
+ dirfd (dest_dir), dest_basename) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+ }
+
+ ret = TRUE;
+ out:
+ if (src_dir != NULL)
+ closedir (src_dir);
+ if (dest_dir != NULL)
+ closedir (dest_dir);
+ g_free (src_basename);
+ g_free (src_dirname);
+ g_free (dest_basename);
+ g_free (tmp_dest_basename);
+ g_free (dest_dirname);
+ g_free (dest_path);
+ return ret;
+}
+
+static gboolean
+archive_file_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ GFile *infile = NULL;
+ GFile *outfile = NULL;
+ GFileOutputStream *out = NULL;
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ char *dest_tmp_path = NULL;
+
+ infile = ot_util_new_file_for_path (path);
+
+ dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+ if (!dest_path)
+ goto out;
+
+ dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
+
+ outfile = ot_util_new_file_for_path (dest_tmp_path);
+ out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+
+ if (rename (dest_tmp_path, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ g_free (dest_tmp_path);
+ g_clear_object (&infile);
+ g_clear_object (&outfile);
+ g_clear_object (&out);
+ return ret;
+}
+
+gboolean
+ostree_repo_store_object_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
+ return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+ else
+ return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+}
+
+gboolean
+ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GString *tempfile_path = NULL;
+ GChecksum *checksum = NULL;
+ gboolean did_exist;
+
+ tempfile_path = g_string_new (priv->path);
+ g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
+
+ if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error))
+ goto out;
+
+ if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted object %s (actual checksum is %s)",
+ expected_checksum, g_checksum_get_string (checksum));
+ goto out;
+ }
+
+ if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path,
+ expected_checksum,
+ objtype,
+ TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (tempfile_path)
+ {
+ (void) unlink (tempfile_path->str);
+ g_string_free (tempfile_path, TRUE);
+ }
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+typedef struct _ParsedTreeData ParsedTreeData;
+typedef struct _ParsedDirectoryData ParsedDirectoryData;
+
+static void parsed_tree_data_free (ParsedTreeData *pdata);
+
+struct _ParsedDirectoryData {
+ ParsedTreeData *tree_data;
+ char *metadata_sha256;
+ GVariant *meta_data;
+};
+
+static void
+parsed_directory_data_free (ParsedDirectoryData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ parsed_tree_data_free (pdata->tree_data);
+ g_free (pdata->metadata_sha256);
+ g_variant_unref (pdata->meta_data);
+ g_free (pdata);
+}
+
+struct _ParsedTreeData {
+ GHashTable *files; /* char* filename -> char* checksum */
+ GHashTable *directories; /* char* dirname -> ParsedDirectoryData* */
+};
+
+static ParsedTreeData *
+parsed_tree_data_new (void)
+{
+ ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
+ ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)parsed_directory_data_free);
+ return ret;
+}
+
+static void
+parsed_tree_data_free (ParsedTreeData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ g_hash_table_destroy (pdata->files);
+ g_hash_table_destroy (pdata->directories);
+ g_free (pdata);
+}
+
+static GVariant *
+create_empty_gvariant_dict (void)
+{
+ GVariantBuilder builder;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
+ return g_variant_builder_end (&builder);
+}
+
+static gboolean
+import_parsed_tree (OstreeRepo *self,
+ ParsedTreeData *tree,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *serialized_tree = NULL;
+ gboolean builders_initialized = FALSE;
+ GVariantBuilder files_builder;
+ GVariantBuilder dirs_builder;
+ GHashTableIter hash_iter;
+ GSList *sorted_filenames = NULL;
+ GSList *iter;
+ gpointer key, value;
+
+ g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
+ g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
+ builders_initialized = TRUE;
+
+ g_hash_table_iter_init (&hash_iter, tree->files);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
+ }
+
+ sorted_filenames = g_slist_sort (sorted_filenames, (GCompareFunc)strcmp);
+
+ for (iter = sorted_filenames; iter; iter = iter->next)
+ {
+ const char *name = iter->data;
+ const char *value;
+
+ value = g_hash_table_lookup (tree->files, name);
+ g_variant_builder_add (&files_builder, "(ss)", name, value);
+ }
+
+ g_slist_free (sorted_filenames);
+ sorted_filenames = NULL;
+
+ g_hash_table_iter_init (&hash_iter, tree->directories);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
+ }
+
+ sorted_filenames = g_slist_sort (sorted_filenames, (GCompareFunc)strcmp);
+
+ for (iter = sorted_filenames; iter; iter = iter->next)
+ {
+ const char *name = iter->data;
+ GChecksum *dir_checksum = NULL;
+ ParsedDirectoryData *dir;
+
+ dir = g_hash_table_lookup (tree->directories, name);
+
+ if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
+ goto out;
+
+ g_variant_builder_add (&dirs_builder, "(sss)",
+ name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
+ g_checksum_free (dir_checksum);
+ }
+
+ g_slist_free (sorted_filenames);
+ sorted_filenames = NULL;
+
+ serialized_tree = g_variant_new ("(u@a{sv}@a(ss)@a(sss))",
+ GUINT32_TO_BE (0),
+ create_empty_gvariant_dict (),
+ g_variant_builder_end (&files_builder),
+ g_variant_builder_end (&dirs_builder));
+ builders_initialized = FALSE;
+ g_variant_ref_sink (serialized_tree);
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_slist_free (sorted_filenames);
+ if (builders_initialized)
+ {
+ g_variant_builder_clear (&files_builder);
+ g_variant_builder_clear (&dirs_builder);
+ }
+ if (serialized_tree)
+ g_variant_unref (serialized_tree);
+ return ret;
+}
+
+static gboolean
+check_path (const char *filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (!*filename)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty filename");
+ goto out;
+ }
+
+ if (strcmp (filename, ".") == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Self-reference '.' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (ot_util_filename_has_dotdot (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (g_path_is_absolute (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Absolute filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+add_one_directory_to_tree_and_import (OstreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ ParsedDirectoryData **dir, /*inout*/
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *dirmeta = NULL;
+ GChecksum *dir_meta_checksum = NULL;
+ ParsedDirectoryData *dir_value = *dir;
+
+ g_assert (tree != NULL);
+
+ if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
+ goto out;
+
+ if (dir_value)
+ {
+ g_variant_unref (dir_value->meta_data);
+ dir_value->meta_data = dirmeta;
+ }
+ else
+ {
+ dir_value = g_new0 (ParsedDirectoryData, 1);
+ dir_value->tree_data = parsed_tree_data_new ();
+ dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
+ dir_value->meta_data = dirmeta;
+ g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
+ }
+
+ ret = TRUE;
+ *dir = dir_value;
+ out:
+ if (dir_meta_checksum)
+ g_checksum_free (dir_meta_checksum);
+ return ret;
+}
+
+static gboolean
+add_one_file_to_tree_and_import (OstreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *checksum = NULL;
+ struct stat stbuf;
+ gboolean did_exist;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_assert (tree != NULL);
+
+ if (!ostree_stat_and_checksum_file (-1, abspath, OSTREE_OBJECT_TYPE_FILE, &checksum, &stbuf, error))
+ goto out;
+
+ if (!ostree_repo_store_object_trusted (self, abspath, g_checksum_get_string (checksum),
+ OSTREE_OBJECT_TYPE_FILE, TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ g_hash_table_replace (tree->files, g_strdup (basename),
+ g_strdup (g_checksum_get_string (checksum)));
+
+ ret = TRUE;
+ out:
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+static gboolean
+add_one_path_to_tree_and_import (OstreeRepo *self,
+ const char *base,
+ const char *filename,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *components = NULL;
+ struct stat stbuf;
+ char *component_abspath = NULL;
+ ParsedTreeData *current_tree = tree;
+ const char *component = NULL;
+ const char *file_sha1;
+ char *abspath = NULL;
+ ParsedDirectoryData *dir;
+ int i;
+ gboolean is_directory;
+
+ if (!check_path (filename, error))
+ goto out;
+
+ abspath = g_build_filename (base, filename, NULL);
+
+ if (lstat (abspath, &stbuf) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ is_directory = S_ISDIR(stbuf.st_mode);
+
+ if (components)
+ g_ptr_array_free (components, TRUE);
+ components = ot_util_path_split (filename);
+ g_assert (components->len > 0);
+
+ current_tree = tree;
+ for (i = 0; i < components->len; i++)
+ {
+ component = components->pdata[i];
+ g_free (component_abspath);
+ component_abspath = ot_util_path_join_n (base, components, i);
+ file_sha1 = g_hash_table_lookup (current_tree->files, component);
+ dir = g_hash_table_lookup (current_tree->directories, component);
+
+ g_assert_cmpstr (component, !=, ".");
+
+ if (i < components->len - 1)
+ {
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered non-directory '%s' in '%s'",
+ component,
+ filename);
+ goto out;
+ }
+ /* Implicitly add intermediate directories */
+ if (!add_one_directory_to_tree_and_import (self, component,
+ component_abspath, current_tree, &dir,
+ error))
+ goto out;
+ g_assert (dir != NULL);
+ current_tree = dir->tree_data;
+ }
+ else if (is_directory)
+ {
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_directory_to_tree_and_import (self, component,
+ abspath, current_tree, &dir,
+ error))
+ goto out;
+ }
+ else
+ {
+ g_assert (!is_directory);
+ if (dir != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_file_to_tree_and_import (self, component, abspath,
+ current_tree, error))
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (components)
+ g_ptr_array_unref (components);
+ g_free (component_abspath);
+ g_free (abspath);
+ return ret;
+}
+
+gboolean
+ostree_repo_write_ref (OstreeRepo *self,
+ gboolean is_local,
+ const char *name,
+ const char *rev,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir,
+ name, rev, error);
+}
+
+static gboolean
+commit_parsed_tree (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ ParsedDirectoryData *root,
+ GChecksum **out_commit,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *root_checksum = NULL;
+ GChecksum *ret_commit = NULL;
+ GVariant *commit = NULL;
+ GDateTime *now = NULL;
+
+ g_assert (branch != NULL);
+ g_assert (subject != NULL);
+
+ if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
+ goto out;
+
+ now = g_date_time_new_now_utc ();
+ commit = g_variant_new ("(u@a{sv}ssstss)",
+ GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
+ metadata ? metadata : create_empty_gvariant_dict (),
+ parent ? parent : "",
+ subject, body ? body : "",
+ GUINT64_TO_BE (g_date_time_to_unix (now)),
+ g_checksum_get_string (root_checksum),
+ root->metadata_sha256);
+ g_variant_ref_sink (commit);
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ commit, &ret_commit, error))
+ goto out;
+
+ if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit;
+ out:
+ if (root_checksum)
+ g_checksum_free (root_checksum);
+ if (commit)
+ g_variant_unref (commit);
+ if (now)
+ g_date_time_unref (now);
+ return ret;
+}
+
+static gboolean
+import_root (OstreeRepo *self,
+ const char *base,
+ ParsedDirectoryData **out_root,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ ParsedDirectoryData *ret_root = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+
+ if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+ goto out;
+
+ ret_root = g_new0 (ParsedDirectoryData, 1);
+ ret_root->tree_data = parsed_tree_data_new ();
+ ret_root->meta_data = root_metadata;
+ root_metadata = NULL;
+ ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+
+ ret = TRUE;
+ *out_root = ret_root;
+ ret_root = NULL;
+ out:
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ parsed_directory_data_free (ret_root);
+ return ret;
+}
+
+gboolean
+ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ int fd,
+ char separator,
+ GChecksum **out_commit,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ ParsedDirectoryData *root = NULL;
+ GChecksum *ret_commit_checksum = NULL;
+ GUnixInputStream *in = NULL;
+ GDataInputStream *datain = NULL;
+ char *filename = NULL;
+ gsize filename_len;
+ GError *temp_error = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+ char *current_head = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+ g_return_val_if_fail (branch != NULL, FALSE);
+ g_return_val_if_fail (subject != NULL, FALSE);
+ g_return_val_if_fail (metadata == NULL || g_variant_is_of_type (metadata, G_VARIANT_TYPE ("a{sv}")), FALSE);
+
+ if (parent == NULL)
+ parent = branch;
+
+ /* We're overwriting the tree */
+ if (!import_root (self, base, &root, error))
+ goto out;
+
+ if (!resolve_rev (self, parent, TRUE, ¤t_head, error))
+ goto out;
+
+ in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
+ datain = g_data_input_stream_new ((GInputStream*)in);
+
+ while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
+ &filename_len, NULL, &temp_error)) != NULL)
+ {
+ if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
+ {
+ if (temp_error != NULL)
+ {
+ g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+ goto out;
+ }
+ }
+ if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
+ goto out;
+ g_free (filename);
+ filename = NULL;
+ }
+ if (filename == NULL && temp_error != NULL)
+ {
+ g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+ goto out;
+ }
+ if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
+ root, &ret_commit_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit_checksum;
+ ret_commit_checksum = NULL;
+ out:
+ if (ret_commit_checksum)
+ g_checksum_free (ret_commit_checksum);
+ g_free (current_head);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ g_clear_object (&datain);
+ g_clear_object (&in);
+ g_free (filename);
+ parsed_directory_data_free (root);
+ return ret;
+
+}
+
+static gboolean
+iter_object_dir (OstreeRepo *self,
+ GFile *dir,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ GFileEnumerator *enumerator = NULL;
+ GFileInfo *file_info = NULL;
+ char *dirpath = NULL;
+
+ dirpath = g_file_get_path (dir);
+
+ enumerator = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (type != G_FILE_TYPE_DIRECTORY
+ && (g_str_has_suffix (name, ".meta")
+ || g_str_has_suffix (name, ".file")
+ || g_str_has_suffix (name, ".packfile")))
+ {
+ char *dot;
+ char *path;
+
+ dot = strrchr (name, '.');
+ g_assert (dot);
+
+ if ((dot - name) == 62)
+ {
+ path = g_build_filename (dirpath, name, NULL);
+ callback (self, path, file_info, user_data);
+ g_free (path);
+ }
+ }
+
+ g_object_unref (file_info);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (!g_file_enumerator_close (enumerator, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dirpath);
+ return ret;
+}
+
+gboolean
+ostree_repo_iter_objects (OstreeRepo *self,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GFile *objectdir = NULL;
+ GFileEnumerator *enumerator = NULL;
+ gboolean ret = FALSE;
+ GFileInfo *file_info = NULL;
+ GError *temp_error = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ objectdir = ot_util_new_file_for_path (priv->objects_path);
+ enumerator = g_file_enumerate_children (objectdir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
+ {
+ GFile *objdir = g_file_get_child (objectdir, name);
+ if (!iter_object_dir (self, objdir, callback, user_data, error))
+ {
+ g_object_unref (objdir);
+ goto out;
+ }
+ g_object_unref (objdir);
+ }
+ g_object_unref (file_info);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (!g_file_enumerator_close (enumerator, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&file_info);
+ g_clear_object (&enumerator);
+ g_clear_object (&objectdir);
+ return ret;
+}
+
+gboolean
+ostree_repo_load_variant (OstreeRepo *self,
+ const char *sha256,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeSerializedVariantType ret_type;
+ GVariant *ret_variant = NULL;
+ char *path = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ path = ostree_repo_get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
+ if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
+ goto out;
+
+ ret = TRUE;
+ *out_type = ret_type;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ g_free (path);
+ return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo *self,
+ OstreeRepoFile *dir,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean
+checkout_one_directory (OstreeRepo *self,
+ const char *destination,
+ const char *dirname,
+ OstreeRepoFile *dir,
+ GFileInfo *dir_info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ GVariant *xattr_variant = NULL;
+
+ dest_path = g_build_filename (destination, dirname, NULL);
+
+ if (!_ostree_repo_file_get_xattrs (dir, &xattr_variant, NULL, error))
+ goto out;
+
+ if (mkdir (dest_path, (mode_t)g_file_info_get_attribute_uint32 (dir_info, "unix::mode")) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
+ goto out;
+ }
+
+ if (!ostree_set_xattrs (dest_path, xattr_variant, cancellable, error))
+ goto out;
+
+ if (!checkout_tree (self, dir, dest_path, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ if (xattr_variant)
+ g_variant_unref (xattr_variant);
+ return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo *self,
+ OstreeRepoFile *dir,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ GFileInfo *file_info = NULL;
+ GFileEnumerator *dir_enum = NULL;
+ GFile *child = NULL;
+ char *object_path = NULL;
+ char *dest_path = NULL;
+
+ dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable,
+ error);
+ if (!dir_enum)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ child = g_file_get_child ((GFile*)dir, name);
+
+ if (type == G_FILE_TYPE_DIRECTORY)
+ {
+ if (!checkout_one_directory (self, destination, name, (OstreeRepoFile*)child, file_info, cancellable, error))
+ goto out;
+ }
+ else
+ {
+ const char *checksum = _ostree_repo_file_nontree_get_checksum ((OstreeRepoFile*)child);
+
+ dest_path = g_build_filename (destination, name, NULL);
+ object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
+
+ if (priv->archive)
+ {
+ if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+ goto out;
+ }
+ else
+ {
+ if (link (object_path, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ }
+
+ g_free (object_path);
+ object_path = NULL;
+ g_free (dest_path);
+ dest_path = NULL;
+ g_clear_object (&file_info);
+ g_clear_object (&child);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&dir_enum);
+ g_clear_object (&file_info);
+ g_clear_object (&child);
+ g_free (object_path);
+ g_free (dest_path);
+ return ret;
+}
+
+gboolean
+ostree_repo_checkout (OstreeRepo *self,
+ const char *rev,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *resolved = NULL;
+ OstreeRepoFile *root = NULL;
+ GFileInfo *root_info = NULL;
+
+ if (g_file_test (destination, G_FILE_TEST_EXISTS))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Destination path '%s' already exists",
+ destination);
+ goto out;
+ }
+
+ if (!resolve_rev (self, rev, FALSE, &resolved, error))
+ goto out;
+
+ root = (OstreeRepoFile*)_ostree_repo_file_new_root (self, resolved);
+ if (!_ostree_repo_file_ensure_resolved (root, error))
+ goto out;
+
+ root_info = g_file_query_info ((GFile*)root, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error);
+ if (!root_info)
+ goto out;
+
+ if (!checkout_one_directory (self, destination, NULL, root, root_info, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (resolved);
+ g_clear_object (&root);
+ g_clear_object (&root_info);
+ return ret;
+}
+
+gboolean
+ostree_repo_diff (OstreeRepo *self,
+ const char *ref,
+ GFile *target,
+ GPtrArray **out_modified,
+ GPtrArray **out_removed,
+ GPtrArray **out_added,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *ret_modified = NULL;
+ GPtrArray *ret_removed = NULL;
+ GPtrArray *ret_added = NULL;
+
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Not implemented yet");
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (ret_modified)
+ g_ptr_array_free (ret_modified, TRUE);
+ if (ret_removed)
+ g_ptr_array_free (ret_removed, TRUE);
+ if (ret_added)
+ g_ptr_array_free (ret_added, TRUE);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_REPO
+#define _OSTREE_REPO
+
+#include "ostree-core.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO ostree_repo_get_type()
+#define OSTREE_REPO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
+#define OSTREE_REPO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
+#define OSTREE_IS_REPO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
+#define OSTREE_IS_REPO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
+#define OSTREE_REPO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
+
+typedef struct {
+ GObject parent;
+} OstreeRepo;
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeRepoClass;
+
+GType ostree_repo_get_type (void);
+
+OstreeRepo* ostree_repo_new (const char *path);
+
+gboolean ostree_repo_check (OstreeRepo *self, GError **error);
+
+const char * ostree_repo_get_path (OstreeRepo *self);
+
+gboolean ostree_repo_is_archive (OstreeRepo *self);
+
+GKeyFile * ostree_repo_get_config (OstreeRepo *self);
+
+GKeyFile * ostree_repo_copy_config (OstreeRepo *self);
+
+gboolean ostree_repo_write_config (OstreeRepo *self,
+ GKeyFile *new_config,
+ GError **error);
+
+char * ostree_repo_get_object_path (OstreeRepo *self,
+ const char *object,
+ OstreeObjectType type);
+
+gboolean ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ GError **error);
+
+gboolean ostree_repo_store_object_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error);
+
+gboolean ostree_repo_resolve_rev (OstreeRepo *self,
+ const char *rev,
+ char **out_resolved,
+ GError **error);
+
+gboolean ostree_repo_write_ref (OstreeRepo *self,
+ gboolean is_local,
+ const char *name,
+ const char *rev,
+ GError **error);
+
+gboolean ostree_repo_load_variant (OstreeRepo *self,
+ const char *sha256,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_repo_load_variant_checked (OstreeRepo *self,
+ OstreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ int fd,
+ char separator,
+ GChecksum **out_commit,
+ GError **error);
+
+gboolean ostree_repo_checkout (OstreeRepo *self,
+ const char *ref,
+ const char *destination,
+ GCancellable *cancellable,
+ GError **error);
+
+typedef struct {
+ guint content_differs : 1;
+ guint xattrs_differs : 1;
+ guint unused : 30;
+
+ GFileInfo *src_info;
+ GFileInfo *target_info;
+
+ char *src_file_checksum;
+ char *target_file_checksum;
+
+ GVariant *src_xattrs;
+ GVariant *target_xattrs;
+} OstreeRepoDiffItem;
+
+gboolean ostree_repo_diff (OstreeRepo *self,
+ const char *ref,
+ GFile *target,
+ GPtrArray **out_modified, /* OstreeRepoDiffItem */
+ GPtrArray **out_removed, /* OstreeRepoDiffItem */
+ GPtrArray **out_added, /* OstreeRepoDiffItem */
+ GCancellable *cancellable,
+ GError **error);
+
+typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
+ GFileInfo *fileinfo, gpointer user_data);
+
+gboolean ostree_repo_iter_objects (OstreeRepo *self,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_REPO */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_H__
+
+#include <ostree-core.h>
+#include <ostree-repo.h>
+#include <ostree-checkout.h>
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+gboolean
+ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
+{
+ GFile *dir;
+ GError *temp_error = NULL;
+ gboolean ret = FALSE;
+
+ dir = g_file_new_for_path (path);
+ if (with_parents)
+ ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
+ else
+ ret = g_file_make_directory (dir, NULL, &temp_error);
+ if (!ret)
+ {
+ if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ else
+ g_clear_error (&temp_error);
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&dir);
+ return ret;
+}
+
+
+char *
+ot_util_get_file_contents_utf8 (const char *path,
+ GError **error)
+{
+ GFile *file = NULL;
+ char *ret = NULL;
+
+ file = ot_util_new_file_for_path (path);
+ if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
+ goto out;
+
+ out:
+ g_clear_object (&file);
+ return ret;
+}
+
+gboolean
+ot_util_gfile_load_contents_utf8 (GFile *file,
+ GCancellable *cancellable,
+ char **contents_out,
+ char **etag_out,
+ GError **error)
+{
+ char *ret_contents = NULL;
+ char *ret_etag = NULL;
+ gsize len;
+ gboolean ret = FALSE;
+
+ if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
+ goto out;
+ if (!g_utf8_validate (ret_contents, len, NULL))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Invalid UTF-8");
+ goto out;
+ }
+
+ if (contents_out)
+ *contents_out = ret_contents;
+ else
+ g_free (ret_contents);
+ ret_contents = NULL;
+ if (etag_out)
+ *etag_out = ret_etag;
+ else
+ g_free (ret_etag);
+ ret_etag = NULL;
+ ret = TRUE;
+ out:
+ g_free (ret_contents);
+ g_free (ret_etag);
+ return ret;
+}
+
+GInputStream *
+ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
+{
+ GInputStream *ret = NULL;
+ int fd;
+ int flags = O_RDONLY;
+ char *path = NULL;
+
+ path = g_file_get_path (file);
+#ifdef O_NOATIME
+ flags |= O_NOATIME;
+#endif
+ fd = open (path, flags);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
+
+ out:
+ g_free (path);
+ return ret;
+}
+
+/* Like g_file_new_for_path, but only do local stuff, not GVFS */
+GFile *
+ot_util_new_file_for_path (const char *path)
+{
+ return g_vfs_get_file_for_path (g_vfs_get_local (), path);
+}
+
+const char *
+ot_gfile_get_path_cached (GFile *file)
+{
+ const char *path;
+
+ path = g_object_get_data ((GObject*)file, "ostree-file-path");
+ if (!path)
+ {
+ path = g_file_get_path (file);
+ g_object_set_data_full ((GObject*)file, "ostree-file-path", (char*)path, (GDestroyNotify)g_free);
+ }
+ return path;
+}
+
+
+const char *
+ot_gfile_get_basename_cached (GFile *file)
+{
+ const char *name;
+
+ name = g_object_get_data ((GObject*)file, "ostree-file-name");
+ if (!name)
+ {
+ name = g_file_get_basename (file);
+ g_object_set_data_full ((GObject*)file, "ostree-file-name", (char*)name, (GDestroyNotify)g_free);
+ }
+ return name;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GIO_UTILS_H__
+#define __OSTREE_GIO_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GFile *ot_util_new_file_for_path (const char *path);
+
+const char *ot_gfile_get_path_cached (GFile *file);
+
+const char *ot_gfile_get_basename_cached (GFile *file);
+
+gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
+
+char * ot_util_get_file_contents_utf8 (const char *path, GError **error);
+
+gboolean ot_util_gfile_load_contents_utf8 (GFile *file,
+ GCancellable *cancellable,
+ char **contents_out,
+ char **etag_out,
+ GError **error);
+
+GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+#if GLIB_CHECK_VERSION(2,32,0)
+/* nothing */
+#else
+/* Code copied from glib/glib/genviron.c */
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+static gint
+ot_g_environ_find (gchar **envp,
+ const gchar *variable)
+{
+ gint len, i;
+
+ len = strlen (variable);
+
+ for (i = 0; envp[i]; i++)
+ {
+ if (strncmp (envp[i], variable, len) == 0 &&
+ envp[i][len] == '=')
+ return i;
+ }
+
+ return -1;
+}
+
+const gchar *
+ot_g_environ_getenv (gchar **envp,
+ const gchar *variable)
+{
+ gint index;
+
+ g_return_val_if_fail (envp != NULL, NULL);
+ g_return_val_if_fail (variable != NULL, NULL);
+
+ index = ot_g_environ_find (envp, variable);
+ if (index != -1)
+ return envp[index] + strlen (variable) + 1;
+ else
+ return NULL;
+}
+
+gchar **
+ot_g_environ_setenv (gchar **envp,
+ const gchar *variable,
+ const gchar *value,
+ gboolean overwrite)
+{
+ gint index;
+
+ g_return_val_if_fail (envp != NULL, NULL);
+ g_return_val_if_fail (variable != NULL, NULL);
+ g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+
+ index = ot_g_environ_find (envp, variable);
+ if (index != -1)
+ {
+ if (overwrite)
+ {
+ g_free (envp[index]);
+ envp[index] = g_strdup_printf ("%s=%s", variable, value);
+ }
+ }
+ else
+ {
+ gint length;
+
+ length = g_strv_length (envp);
+ envp = g_renew (gchar *, envp, length + 2);
+ envp[length] = g_strdup_printf ("%s=%s", variable, value);
+ envp[length + 1] = NULL;
+ }
+
+ return envp;
+}
+
+gchar **
+ot_g_environ_unsetenv (gchar **envp,
+ const gchar *variable)
+{
+ gint len;
+ gchar **e, **f;
+
+ g_return_val_if_fail (envp != NULL, NULL);
+ g_return_val_if_fail (variable != NULL, NULL);
+ g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+
+ len = strlen (variable);
+
+ /* Note that we remove *all* environment entries for
+ * the variable name, not just the first.
+ */
+ e = f = envp;
+ while (*e != NULL)
+ {
+ if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
+ {
+ *f = *e;
+ f++;
+ }
+ e++;
+ }
+ *f = NULL;
+
+ return envp;
+}
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GLIB_COMPAT_H__
+#define __OSTREE_GLIB_COMPAT_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if GLIB_CHECK_VERSION(2,32,0)
+#define ot_g_environ_getenv g_environ_getenv
+#define ot_g_environ_setenv g_environ_setenv
+#define ot_g_environ_unsetenv g_environ_unsetenv
+#else
+const gchar *
+ot_g_environ_getenv (gchar **envp,
+ const gchar *variable);
+
+gchar **
+ot_g_environ_setenv (gchar **envp,
+ const gchar *variable,
+ const gchar *value,
+ gboolean overwrite);
+
+gchar **
+ot_g_environ_unsetenv (gchar **envp,
+ const gchar *variable);
+#endif
+
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+void
+ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
+{
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_OPT_UTILS_H__
+#define __OSTREE_OPT_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-unix-utils.h"
+
+#include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+gboolean
+ot_util_spawn_pager (GOutputStream **out_stream,
+ GError **error)
+{
+ const char *pager;
+ char *argv[2];
+ int stdin_fd;
+ pid_t pid;
+ gboolean ret = FALSE;
+ GOutputStream *ret_stream = NULL;
+
+ if (!isatty (1))
+ {
+ ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
+ }
+ else
+ {
+ pager = g_getenv ("GIT_PAGER");
+ if (pager == NULL)
+ pager = "less";
+
+ argv[0] = (char*)pager;
+ argv[1] = NULL;
+
+ if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
+ {
+ g_prefix_error (error, "%s", "Failed to spawn pager: ");
+ goto out;
+ }
+
+ ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
+ }
+
+ *out_stream = ret_stream;
+ ret_stream = NULL;
+ ret = TRUE;
+ out:
+ g_clear_object (&ret_stream);
+ return ret;
+}
+
+static int
+compare_filenames_by_component_length (const char *a,
+ const char *b)
+{
+ char *a_slash, *b_slash;
+
+ a_slash = strchr (a, '/');
+ b_slash = strchr (b, '/');
+ while (a_slash && b_slash)
+ {
+ a = a_slash + 1;
+ b = b_slash + 1;
+ a_slash = strchr (a, '/');
+ b_slash = strchr (b, '/');
+ }
+ if (a_slash)
+ return -1;
+ else if (b_slash)
+ return 1;
+ else
+ return 0;
+}
+
+GPtrArray *
+ot_util_sort_filenames_by_component_length (GPtrArray *files)
+{
+ GPtrArray *array = g_ptr_array_sized_new (files->len);
+ memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
+ g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
+ return array;
+}
+
+int
+ot_util_count_filename_components (const char *path)
+{
+ int i = 0;
+
+ while (path)
+ {
+ i++;
+ path = strchr (path, '/');
+ if (path)
+ path++;
+ }
+ return i;
+}
+
+gboolean
+ot_util_filename_has_dotdot (const char *path)
+{
+ char *p;
+ char last;
+
+ if (strcmp (path, "..") == 0)
+ return TRUE;
+ if (g_str_has_prefix (path, "../"))
+ return TRUE;
+ p = strstr (path, "/..");
+ if (!p)
+ return FALSE;
+ last = *(p + 1);
+ return last == '\0' || last == '/';
+}
+
+GPtrArray *
+ot_util_path_split (const char *path)
+{
+ GPtrArray *ret = NULL;
+ const char *p;
+ const char *slash;
+ int i;
+
+ g_return_val_if_fail (path[0] != '/', NULL);
+
+ ret = g_ptr_array_new ();
+ g_ptr_array_set_free_func (ret, g_free);
+
+ p = path;
+ do {
+ slash = strchr (p, '/');
+ if (!slash)
+ {
+ g_ptr_array_add (ret, g_strdup (p));
+ p = NULL;
+ }
+ else
+ {
+ g_ptr_array_add (ret, g_strndup (p, slash - p));
+ p = slash + 1;
+ }
+ } while (p && *p);
+
+ /* Canonicalize by removing duplicate '.' */
+ for (i = ret->len-1; i >= 0; i--)
+ {
+ if (strcmp (ret->pdata[i], ".") == 0)
+ g_ptr_array_remove_index (ret, i);
+ }
+
+ return ret;
+}
+
+char *
+ot_util_path_join_n (const char *base, GPtrArray *components, int n)
+{
+ int max = MIN(n+1, components->len);
+ GPtrArray *subcomponents;
+ char *path;
+ int i;
+
+ subcomponents = g_ptr_array_new ();
+
+ if (base != NULL)
+ g_ptr_array_add (subcomponents, (char*)base);
+
+ for (i = 0; i < max; i++)
+ {
+ g_ptr_array_add (subcomponents, components->pdata[i]);
+ }
+ g_ptr_array_add (subcomponents, NULL);
+
+ path = g_build_filenamev ((char**)subcomponents->pdata);
+ g_ptr_array_free (subcomponents, TRUE);
+
+ return path;
+}
+
+void
+ot_util_set_error_from_errno (GError **error,
+ gint saved_errno)
+{
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ 0,
+ g_strerror (saved_errno));
+ errno = saved_errno;
+}
+
+int
+ot_util_open_file_read (const char *path, GError **error)
+{
+ char *dirname = NULL;
+ char *basename = NULL;
+ DIR *dir = NULL;
+ int fd = -1;
+
+ dirname = g_path_get_dirname (path);
+ basename = g_path_get_basename (path);
+ dir = opendir (dirname);
+ if (dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
+
+ out:
+ g_free (basename);
+ g_free (dirname);
+ if (dir != NULL)
+ closedir (dir);
+ return fd;
+}
+
+int
+ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
+{
+ int fd;
+ int flags = O_RDONLY;
+
+#ifdef O_CLOEXEC
+ flags |= O_CLOEXEC;
+#endif
+#ifdef O_NOATIME
+ flags |= O_NOATIME;
+#endif
+ fd = openat (dirfd, name, flags);
+ if (fd < 0)
+ ot_util_set_error_from_errno (error, errno);
+ return fd;
+}
+
+void
+ot_util_fatal_literal (const char *msg)
+{
+ g_printerr ("%s\n", msg);
+ exit (1);
+}
+
+void
+ot_util_fatal_gerror (GError *error)
+{
+ g_assert (error != NULL);
+ ot_util_fatal_literal (error->message);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UNIX_UTILS_H__
+#define __OSTREE_UNIX_UTILS_H__
+
+#include <gio/gio.h>
+
+/* I just put all this shit here. Sue me. */
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+G_BEGIN_DECLS
+
+gboolean ot_util_spawn_pager (GOutputStream **out_stream, GError **error);
+
+void ot_util_fatal_literal (const char *msg) G_GNUC_NORETURN;
+
+void ot_util_fatal_gerror (GError *error) G_GNUC_NORETURN;
+
+gboolean ot_util_filename_has_dotdot (const char *path);
+
+GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
+
+GPtrArray* ot_util_path_split (const char *path);
+
+char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
+
+int ot_util_count_filename_components (const char *path);
+
+int ot_util_open_file_read (const char *path, GError **error);
+
+int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
+
+void ot_util_set_error_from_errno (GError **error, gint saved_errno);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+GHashTable *
+ot_util_variant_asv_to_hash_table (GVariant *variant)
+{
+ GHashTable *ret;
+ GVariantIter *viter;
+ char *key;
+ GVariant *value;
+
+ ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
+ viter = g_variant_iter_new (variant);
+ while (g_variant_iter_next (viter, "{s@v}", &key, &value))
+ g_hash_table_replace (ret, key, g_variant_ref_sink (value));
+
+ g_variant_iter_free (viter);
+
+ return ret;
+}
+
+gboolean
+ot_util_variant_save (GFile *dest,
+ GVariant *variant,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GOutputStream *out = NULL;
+ gsize bytes_written;
+
+ out = (GOutputStream*)g_file_replace (dest, NULL, 0, FALSE, cancellable, error);
+ if (!out)
+ goto out;
+
+ if (!g_output_stream_write_all (out,
+ g_variant_get_data (variant),
+ g_variant_get_size (variant),
+ &bytes_written,
+ cancellable,
+ error))
+ goto out;
+ if (!g_output_stream_close (out, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&out);
+ return ret;
+}
+
+GVariant *
+ot_util_variant_take_ref (GVariant *variant)
+{
+#if GLIB_CHECK_VERSION(2,32,0)
+ return g_variant_take_ref (variant);
+#else
+ if (g_variant_is_floating (variant))
+ return g_variant_ref_sink (variant);
+ return variant;
+#endif
+}
+
+gboolean
+ot_util_variant_map (GFile *src,
+ const GVariantType *type,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GMappedFile *mfile = NULL;
+ char *path = NULL;
+ GVariant *ret_variant = NULL;
+
+ path = g_file_get_path (src);
+ mfile = g_mapped_file_new (path, FALSE, error);
+ if (!mfile)
+ goto out;
+
+ ret_variant = g_variant_new_from_data (type,
+ g_mapped_file_get_contents (mfile),
+ g_mapped_file_get_length (mfile),
+ FALSE,
+ (GDestroyNotify) g_mapped_file_unref,
+ mfile);
+ mfile = NULL;
+ g_variant_ref_sink (ret_variant);
+
+ ret = TRUE;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ if (mfile)
+ g_mapped_file_unref (mfile);
+ g_free (path);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_VARIANT_UTILS_H__
+#define __OSTREE_VARIANT_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GHashTable *ot_util_variant_asv_to_hash_table (GVariant *variant);
+
+GVariant * ot_util_variant_take_ref (GVariant *variant);
+
+gboolean ot_util_variant_save (GFile *dest,
+ GVariant *variant,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ot_util_variant_map (GFile *src,
+ const GVariantType *type,
+ GVariant **out_variant,
+ GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UTIL_H__
+
+#include <ot-gio-utils.h>
+#include <ot-glib-compat.h>
+#include <ot-opt-utils.h>
+#include <ot-unix-utils.h>
+#include <ot-variant-utils.h>
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "ob-builtins.h"
+
+static OsbuildBuiltin builtins[] = {
+ { "buildone", osbuild_builtin_buildone, 0 },
+ { NULL }
+};
+
+static int
+usage (char **argv, gboolean is_error)
+{
+ OsbuildBuiltin *builtin = builtins;
+ void (*print_func) (const gchar *format, ...);
+
+ if (is_error)
+ print_func = g_printerr;
+ else
+ print_func = g_print;
+
+ print_func ("usage: %s COMMAND [options]\n",
+ argv[0]);
+ print_func ("Builtin commands:\n");
+
+ while (builtin->name)
+ {
+ print_func (" %s\n", builtin->name);
+ builtin++;
+ }
+ return (is_error ? 1 : 0);
+}
+
+
+int
+main (int argc,
+ char **argv)
+{
+ OsbuildBuiltin *builtin;
+ const char *cmd;
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+
+ builtin = builtins;
+
+ if (argc < 2)
+ return usage (argv, 1);
+
+ cmd = argv[1];
+
+ while (builtin->name)
+ {
+ GError *error = NULL;
+ if (strcmp (cmd, builtin->name) == 0)
+ {
+ int i;
+ int tmp_argc;
+ char **tmp_argv;
+
+ tmp_argc = argc - 1;
+ tmp_argv = g_new0 (char *, tmp_argc + 1);
+
+ tmp_argv[0] = (char*)builtin->name;
+ for (i = 0; i < tmp_argc; i++)
+ tmp_argv[i+1] = argv[i+2];
+ if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
+ {
+ g_free (tmp_argv);
+ g_printerr ("%s\n", error->message);
+ g_clear_error (&error);
+ return 1;
+ }
+ g_free (tmp_argv);
+ return 0;
+ }
+ builtin++;
+ }
+
+ g_printerr ("Unknown command '%s'\n", cmd);
+ return usage (argv, 1);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "otutil.h"
+#include "ob-builtins.h"
+
+#include <glib/gi18n.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *repo_path;
+static char *ref;
+static char *name;
+static char *generator;
+static char *resultdir;
+static gboolean raw;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { "rev", 'r', 0, G_OPTION_ARG_STRING, &ref, "Build using this tree", "rev" },
+ { "name", 0, 0, G_OPTION_ARG_STRING, &name, "Name of the source", "source" },
+ { "generator", 0, 0, G_OPTION_ARG_FILENAME, &generator, "Script to run on installed tree", "script" },
+ { "raw", 0, 0, G_OPTION_ARG_NONE, &raw, "Do not instantiate a tree, use current", NULL },
+ { "resultdir", 0, 0, G_OPTION_ARG_FILENAME, &resultdir, "Directory for output artifacts", "dir" },
+ { NULL }
+};
+
+static char *
+get_tmpdir (void) G_GNUC_UNUSED;
+
+static char *
+get_tmpdir (void)
+{
+ char *tmp_prefix = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
+ char *ret;
+
+ if (tmp_prefix)
+ {
+ ret = g_strdup_printf ("%s/osbuild", tmp_prefix);
+ }
+ else
+ {
+ tmp_prefix = g_strdup_printf ("/tmp/osbuild-%d", getuid ());
+ if (!g_file_test (tmp_prefix, G_FILE_TEST_IS_DIR))
+ {
+ if (!mkdir (tmp_prefix, 0755))
+ {
+ g_printerr ("Failed to make logging directory %s\n", tmp_prefix);
+ exit (1);
+ }
+ }
+ ret = tmp_prefix;
+ tmp_prefix = NULL;
+ }
+ g_free (tmp_prefix);
+ return ret;
+}
+
+static gboolean
+open_log (const char *name,
+ GOutputStream **out_log,
+ GError **error) G_GNUC_UNUSED;
+
+static gboolean
+open_log (const char *name,
+ GOutputStream **out_log,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *tmpdir = NULL;
+ char *path = NULL;
+ GFile *logf = NULL;
+ GFileOutputStream *ret_log = NULL;
+
+ path = g_strdup_printf ("%s/%s.log", tmpdir, name);
+ logf = ot_util_new_file_for_path (path);
+
+ ret_log = g_file_replace (logf, NULL, FALSE, 0, NULL, error);
+ if (!ret_log)
+ goto out;
+
+ ret = TRUE;
+ *out_log = (GOutputStream*)ret_log;
+ ret_log = NULL;
+ out:
+ g_free (path);
+ g_free (tmpdir);
+ g_clear_object (&logf);
+ g_clear_object (&ret_log);
+ return ret;
+}
+
+gboolean
+osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+
+ context = g_option_context_new ("- Build current directory");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (!raw && !repo_path)
+ {
+ ot_util_usage_error (context, "A result directory must be specified with --resultdir", error);
+ goto out;
+ }
+
+ if (!generator)
+ generator = g_build_filename (LIBEXECDIR, "ostree", "generators", "default", NULL);
+
+
+
+ out:
+ if (context)
+ g_option_context_free (context);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSBUILD_BUILTINS__
+#define __OSBUILD_BUILTINS__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ OSBUILD_BUILTIN_FLAG_NONE = 0,
+} OsbuildBuiltinFlags;
+
+typedef struct {
+ const char *name;
+ gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
+ int flags; /* OsbuildBuiltinFlags */
+} OsbuildBuiltin;
+
+gboolean osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "otutil.h"
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static const char *
+find_first_file (GFileTest test, const char *name, ...) G_GNUC_NULL_TERMINATED;
+
+static const char *
+find_first_file (GFileTest test, const char *name, ...)
+{
+ va_list args;
+
+ va_start (args, name);
+
+ do
+ {
+ if (g_file_test (name, test))
+ break;
+ name = va_arg (args, const char *);
+ }
+ while (name != NULL);
+
+ va_end (args);
+
+ return name;
+}
+
+static void
+split_configure_make_args (int argc,
+ char **argv,
+ GPtrArray **out_configure_args,
+ GPtrArray **out_make_args,
+ GPtrArray **out_makeinstall_args)
+{
+ int i;
+
+ *out_configure_args = g_ptr_array_new ();
+ *out_make_args = g_ptr_array_new ();
+ *out_makeinstall_args = g_ptr_array_new ();
+
+ for (i = 1; i < argc; i++)
+ {
+ if (g_str_has_prefix (argv[i], "--"))
+ g_ptr_array_add (*out_configure_args, argv[i]);
+ else if (g_str_has_prefix (argv[i], "DESTDIR="))
+ g_ptr_array_add (*out_makeinstall_args, argv[i]);
+ else
+ g_ptr_array_add (*out_make_args, argv[i]);
+ }
+}
+
+static void
+spawn_sync_or_fatal (char **args, char **env, GSpawnFlags flags)
+{
+ GError *error = NULL;
+ int estatus;
+ char **iter;
+
+ g_print ("osbuild: running: ");
+ for (iter = args; *iter; iter++)
+ g_print ("%s ", *iter);
+ g_print ("\n");
+ if (g_spawn_sync (NULL, args, env, flags, NULL, NULL, NULL, NULL, &estatus, &error))
+ {
+ if (WIFEXITED (estatus) && WEXITSTATUS (estatus) == 0)
+ {
+ g_message ("Subprocess %s exited successfully\n", args[0]);
+ }
+ else
+ {
+ if (WIFEXITED (estatus))
+ g_error ("Subprocess %s exited with code %d\n", args[0], WEXITSTATUS (estatus));
+ else if (WIFSIGNALED (estatus))
+ g_error ("Subprocess %s killed by signal %d\n", args[0], WTERMSIG (estatus));
+ else
+ g_error ("Subprocess %s terminated with status %d\n", args[0], estatus);
+ exit (1);
+ }
+ }
+ else
+ {
+ g_error ("Failed to execute %s: %s\n", args[0], error->message);
+ exit (1);
+ }
+}
+
+static void
+ptr_array_extend (GPtrArray *dest, GPtrArray *to_append)
+{
+ int i;
+
+ for (i = 0; i < to_append->len; i++)
+ g_ptr_array_add (dest, to_append->pdata[i]);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GPtrArray *config_args;
+ GPtrArray *make_args;
+ GPtrArray *makeinstall_args;
+ GPtrArray *args;
+ char **subprocess_env;
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+
+ args = g_ptr_array_new ();
+
+ subprocess_env = g_get_environ ();
+ ot_g_environ_setenv (subprocess_env, "LANG", "C", TRUE);
+ ot_g_environ_unsetenv (subprocess_env, "LC_ALL");
+
+ split_configure_make_args (argc, argv, &config_args, &make_args, &makeinstall_args);
+
+ if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
+ {
+ const char *autogen;
+ char **autogen_env;
+
+ autogen = find_first_file (G_FILE_TEST_IS_EXECUTABLE, "./autogen", "./autogen.sh", NULL);
+ if (!autogen)
+ ot_util_fatal_literal ("No executable configure or autogen script found");
+
+ autogen_env = g_strdupv (subprocess_env);
+ ot_g_environ_setenv (autogen_env, "NOCONFIGURE", "1", TRUE);
+
+ g_ptr_array_set_size (args, 0);
+ g_ptr_array_add (args, (char*) autogen);
+ g_ptr_array_add (args, NULL);
+ spawn_sync_or_fatal ((char**)args->pdata, autogen_env, 0);
+ }
+
+ if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
+ ot_util_fatal_literal ("autogen script failed to generate a configure script");
+
+ g_ptr_array_set_size (args, 0);
+ g_ptr_array_add (args, "./configure");
+ ptr_array_extend (args, config_args);
+ g_ptr_array_add (args, NULL);
+ spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, 0);
+
+ g_ptr_array_set_size (args, 0);
+ g_ptr_array_add (args, "make");
+ ptr_array_extend (args, make_args);
+ g_ptr_array_add (args, NULL);
+ spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
+
+ g_ptr_array_set_size (args, 0);
+ g_ptr_array_add (args, "make");
+ g_ptr_array_add (args, "install");
+ ptr_array_extend (args, makeinstall_args);
+ g_ptr_array_add (args, NULL);
+ spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
+
+ return 0;
+}
--- /dev/null
+#!/usr/bin/python
+#
+# ostree-buildone:
+# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
+# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
+
+# The build output is automatically logged to $TMPDIR/build-$(PWD).log.
+# For example, invoking metabuild in a directory named "foo" will log
+# to /tmp/build-foo.log
+#
+# You can pass arguments to metabuild; if they start with '--', they're
+# given to configure. Otherwise, they're passed to make.
+#
+# $ metabuild --enable-libfoo # passed to configure
+# $ metabuild -j 1 # passed to make
+
+import os,sys,subprocess,tempfile,re
+import select,time,stat,fcntl
+
+subprocess_nice_args = []
+
+# In the future we should test for this better; possibly implement a
+# custom fork handler
+try:
+ chrt_args = ['chrt', '--idle', '0']
+ proc = subprocess.Popen(chrt_args + ['true'])
+ if proc.wait() == 0:
+ subprocess_nice_args.extend(chrt_args)
+except OSError, e:
+ pass
+
+try:
+ ionice_args = ['ionice', '-c', '3', '-t']
+ proc = subprocess.Popen(ionice_args + ['true'])
+ if proc.wait() == 0:
+ subprocess_nice_args.extend(ionice_args)
+except OSError, e:
+ pass
+
+warning_re = re.compile(r'(: ((warning)|(error)|(fatal error)): )|(make(\[[0-9]+\])?: \*\*\*)')
+output_whitelist_re = re.compile(r'^(make(\[[0-9]+\])?: Entering directory)|(ostree-build )')
+
+_bold_sequence = None
+_normal_sequence = None
+if os.isatty(1):
+ _bold_sequence = subprocess.Popen(['tput', 'bold'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
+ _normal_sequence = subprocess.Popen(['tput', 'sgr0'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
+def _bold(text):
+ if _bold_sequence is not None:
+ return '%s%s%s' % (_bold_sequence, text, _normal_sequence)
+ else:
+ return text
+
+class Mainloop(object):
+ DEFAULT = None
+ def __init__(self):
+ self._running = True
+ self.poll = select.poll()
+ self._timeouts = []
+ self._pid_watches = {}
+ self._fd_callbacks = {}
+
+ @classmethod
+ def get(cls, context):
+ if context is None:
+ if cls.DEFAULT is None:
+ cls.DEFAULT = cls()
+ return cls.DEFAULT
+ raise NotImplementedError("Unknown context %r" % (context, ))
+
+ def watch_fd(self, fd, callback):
+ self.poll.register(fd)
+ self._fd_callbacks[fd] = callback
+
+ def unwatch_fd(self, fd):
+ self.poll.unregister(fd)
+ del self._fd_callbacks[fd]
+
+ def watch_pid(self, pid, callback):
+ self._pid_watches[pid] = callback
+
+ def timeout_add(self, ms, callback):
+ self._timeouts.append((ms, callback))
+
+ def quit(self):
+ self._running = False
+
+ def run_once(self):
+ min_timeout = None
+ if len(self._pid_watches) > 0:
+ min_timeout = 500
+ for (ms, callback) in self._timeouts:
+ if (min_timeout is None) or (ms < min_timeout):
+ min_timeout = ms
+ origtime = time.time() * 1000
+ fds = self.poll.poll(min_timeout)
+ for fd in fds:
+ self._fd_callbacks[fd]()
+ for pid in self._pid_watches:
+ (opid, status) = os.waitpid(pid, os.WNOHANG)
+ if opid != 0:
+ self._pid_watches[pid](opid, status)
+ del self._pid_watches[pid]
+ newtime = time.time() * 1000
+ diff = int(newtime - origtime)
+ if diff < 0: diff = 0
+ for i,(ms, callback) in enumerate(self._timeouts):
+ remaining_ms = ms - diff
+ if remaining_ms <= 0:
+ callback()
+ else:
+ self._timeouts[i] = (remaining_ms, callback)
+
+ def run(self):
+ while self._running:
+ self.run_once()
+
+class FileMonitor(object):
+ def __init__(self):
+ self._paths = {}
+ self._path_modtimes = {}
+ self._timeout = 1000
+ self._timeout_installed = False
+ self._loop = Mainloop.get(None)
+
+ def _stat(self, path):
+ try:
+ st = os.stat(path)
+ return st[stat.ST_MTIME]
+ except OSError, e:
+ return None
+
+ def add(self, path, callback):
+ if path not in self._paths:
+ self._paths[path] = []
+ self._path_modtimes[path] = self._stat(path)
+ self._paths[path].append(callback)
+ if not self._timeout_installed:
+ self._timeout_installed = True
+ self._loop.timeout_add(self._timeout, self._check_files)
+
+ def _check_files(self):
+ for (path,callbacks) in self._paths.iteritems():
+ mtime = self._stat(path)
+ orig_mtime = self._path_modtimes[path]
+ if (mtime is not None) and (orig_mtime is None or (mtime > orig_mtime)):
+ self._path_modtimes[path] = mtime
+ for cb in callbacks:
+ cb()
+
+_filemon = FileMonitor()
+
+class OutputFilter(object):
+ def __init__(self, filename, output):
+ self.filename = filename
+ self.output = output
+
+ # inherit globals
+ self._warning_re = warning_re
+ self._nonfilter_re = output_whitelist_re
+
+ self._buf = ''
+ self._warning_count = 0
+ self._filtered_line_count = 0
+ _filemon.add(filename, self._on_changed)
+ self._fd = os.open(filename, os.O_RDONLY)
+ fcntl.fcntl(self._fd, fcntl.F_SETFL, os.O_NONBLOCK)
+
+ def _do_read(self):
+ while True:
+ buf = os.read(self._fd, 4096)
+ if buf == '':
+ break
+ self._buf += buf
+ self._flush()
+
+ def _write_last_log_lines(self):
+ _last_line_limit = 100
+ f = open(logfile_path)
+ lines = []
+ for line in f:
+ if line.startswith('ostree-build '):
+ continue
+ lines.append(line)
+ if len(lines) > _last_line_limit:
+ lines.pop(0)
+ f.close()
+ for line in lines:
+ self.output.write('| ')
+ self.output.write(line)
+
+ def _flush(self):
+ while True:
+ p = self._buf.find('\n')
+ if p < 0:
+ break
+ line = self._buf[0:p]
+ self._buf = self._buf[p+1:]
+ match = self._warning_re.search(line)
+ if match:
+ self._warning_count += 1
+ self.output.write(line + '\n')
+ else:
+ match = self._nonfilter_re.search(line)
+ if match:
+ self.output.write(line + '\n')
+ else:
+ self._filtered_line_count += 1
+
+ def _on_changed(self):
+ self._do_read()
+
+ def start(self):
+ self._do_read()
+
+ def finish(self, successful):
+ self._do_read()
+ if not successful:
+ self._write_last_log_lines()
+ pass
+ self.output.write("ostree-build %s: %d warnings\n" % ('success' if successful else _bold('failed'),
+ self._warning_count, ))
+ self.output.write("ostree-build: full log path: %s\n" % (logfile_path, ))
+
+ if successful:
+ for f in os.listdir('_build'):
+ path = os.path.join('_build', f)
+ if f.startswith('artifact-'):
+ self.output.write("ostree-build: created artifact: %s\n" % (f, ))
+ sys.exit(0 if successful else 1)
+
+def _on_makeinstall_exit(pid, estatus):
+ _output_filter.finish(estatus == 0)
+
+def _on_make_exit(pid, estatus):
+ if estatus == 0:
+ args = list(subprocess_nice_args)
+ args.append('ostree-buildone-makeinstall-split-artifacts')
+ _logfile_f.write("Running: %r\n" % (args, ))
+ _logfile_f.flush()
+ proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
+ _loop.watch_pid(proc.pid, _on_makeinstall_exit)
+ else:
+ _output_filter.finish(False)
+
+def _get_version():
+ if not os.path.isdir('.git'):
+ sys.stderr.write("ostree-buildone: error: Couldn't find .git directory")
+ sys.exit(1)
+
+ proc = subprocess.Popen(['git', 'describe'], stdout=subprocess.PIPE)
+ output = proc.communicate()[0].strip()
+ if proc.wait() != 0:
+ proc = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
+ if proc.wait() != 0:
+ sys.stderr.write("ostree-buildone: error: git rev-parse HEAD failed")
+ sys.exit(1)
+ output = proc.communicate()[0].strip()
+ return output
+
+if __name__ == '__main__':
+ user_tmpdir = os.environ.get('XDG_RUNTIME_DIR')
+ if user_tmpdir is None:
+ user_tmpdir = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'metabuild-%s' % (os.getuid(), ))
+ else:
+ user_tmpdir = os.path.join(user_tmpdir, 'ostree-build')
+
+ os.environ['OSBUILD_VERSION'] = _get_version()
+
+ if os.path.isdir('_build'):
+ for filename in os.listdir('_build'):
+ path = os.path.join('_build', filename)
+ if filename.startswith('artifact-'):
+ os.unlink(path)
+
+ if not os.path.isdir(user_tmpdir):
+ os.makedirs(user_tmpdir)
+ logfile_path = os.path.join(user_tmpdir, '%s.log' % (os.path.basename(os.getcwd()), ))
+ try:
+ os.unlink(logfile_path)
+ except OSError, e:
+ pass
+ logfile_write_fd = os.open(logfile_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
+ global _logfile_f
+ _logfile_f = os.fdopen(logfile_write_fd, "w")
+ sys.stdout.write('ostree-build: logging to %r\n' % (logfile_path, ))
+ sys.stdout.flush()
+
+ global _output_filter
+ _output_filter = OutputFilter(logfile_path, sys.stdout)
+ _output_filter.start()
+
+ args = list(subprocess_nice_args)
+ args.append('ostree-buildone-make')
+ args.extend(sys.argv[1:])
+ devnull=open('/dev/null')
+ _logfile_f.write("Running: %r\n" % (args, ))
+ _logfile_f.flush()
+ proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
+
+ global _loop
+ _loop = Mainloop.get(None)
+ _loop.watch_pid(proc.pid, _on_make_exit)
+ _loop.run()
--- /dev/null
+#!/usr/bin/python
+
+# ostree-buildone-raw: Generic build system wrapper
+# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
+# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
+
+# ostree-buildone-raw wraps systems that implement the GNOME build API:
+# http://people.gnome.org/~walters/docs/build-api.txt
+
+import os,sys,subprocess,tempfile,re
+from multiprocessing import cpu_count
+import select,time
+
+root = None
+
+prefix = '/usr'
+
+# libdir detection
+if os.path.isdir('/lib64'):
+ libdir=os.path.join(prefix, 'lib64')
+else:
+ libdir=os.path.join(prefix, 'lib')
+
+default_buildapi_jobs = ['-j', '%d' % (cpu_count() * 2, )]
+configargs = ['--prefix=' + prefix,
+ '--libdir=' + libdir,
+ '--sysconfdir=/etc',
+ '--localstatedir=/var',
+ '--bindir=' + os.path.join(prefix, 'bin'),
+ '--sbindir=' + os.path.join(prefix, 'sbin'),
+ '--datadir=' + os.path.join(prefix, 'share'),
+ '--includedir=' + os.path.join(prefix, 'include'),
+ '--libexecdir=' + os.path.join(prefix, 'libexec'),
+ '--mandir=' + os.path.join(prefix, 'share', 'man'),
+ '--infodir=' + os.path.join(prefix, 'share', 'info')]
+makeargs = ['make']
+
+top_srcdir=os.getcwd()
+
+for arg in sys.argv[1:]:
+ if arg.startswith('--'):
+ configargs.append(arg)
+ else:
+ makeargs.append(arg)
+
+def log(msg):
+ fullmsg = 'ostree-buildone: ' + msg + '\n'
+ sys.stdout.write(fullmsg)
+ sys.stdout.flush()
+
+def fatal(msg):
+ log(msg)
+ sys.exit(1)
+
+def run_sync(args, env=None):
+ log("Running: %r" % (args, ))
+ f = open('/dev/null', 'r')
+ proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
+ close_fds=True, env=env)
+ f.close()
+ returncode = proc.wait()
+ log("pid %d exited with code %d" % (proc.pid, returncode))
+ if returncode != 0:
+ sys.exit(1)
+
+class BuildSystemScanner(object):
+ @classmethod
+ def _find_file(cls, names):
+ for name in names:
+ if os.path.exists(name):
+ return name
+ return None
+
+ @classmethod
+ def get_configure_source_script(cls):
+ return cls._find_file(('./configure.ac', './configure.in'))
+
+ @classmethod
+ def get_configure_script(cls):
+ return cls._find_file(('./configure', ))
+
+ @classmethod
+ def get_bootstrap_script(cls):
+ return cls._find_file(('./autogen.sh', ))
+
+ @classmethod
+ def get_silent_rules(cls):
+ src = cls.get_configure_source_script()
+ if not src:
+ return False
+ f = open(src)
+ for line in f:
+ if line.find('AM_SILENT_RULES') >= 0:
+ f.close()
+ return True
+ f.close()
+ return False
+
+def _search_file(filename, pattern):
+ f = open(filename)
+ for line in f:
+ if line.startswith(pattern):
+ f.close()
+ return line
+ f.close()
+ return None
+
+def _find_buildapi_makevariable(name):
+ var = '.%s:' % (name, )
+ line = None
+ if os.path.exists('Makefile.in'):
+ line = _search_file('Makefile.in', var)
+ if not line and os.path.exists('Makefile'):
+ line = _search_file('Makefile', var)
+ return line is not None
+
+def phase_bootstrap():
+ have_configure = BuildSystemScanner.get_configure_script()
+ have_configure_source = BuildSystemScanner.get_configure_source_script()
+ if not (have_configure or have_configure_source):
+ fatal("No configure or bootstrap script detected; unknown buildsystem")
+ return
+
+ need_v1 = BuildSystemScanner.get_silent_rules()
+ if need_v1:
+ log("Detected AM_SILENT_RULES, adding --disable-silent-rules to configure")
+ configargs.append('--disable-silent-rules')
+
+ if have_configure:
+ phase_configure()
+ else:
+ bootstrap = BuildSystemScanner.get_bootstrap_script()
+ if bootstrap:
+ log("Detected bootstrap script: %s, using it" % (bootstrap, ))
+ args = [bootstrap]
+ args.extend(configargs)
+ # Add NOCONFIGURE; GNOME style scripts use this
+ env = dict(os.environ)
+ env['NOCONFIGURE'] = '1'
+ run_sync(args, env=env)
+ else:
+ log("No bootstrap script found; using generic autoreconf")
+ run_sync(['autoreconf', '-f', '-i'])
+ phase_configure()
+
+def phase_configure():
+ use_builddir = True
+ doesnot_support_builddir = _find_buildapi_makevariable('buildapi-no-builddir')
+ if doesnot_support_builddir:
+ log("Found .buildapi-no-builddir; copying source tree to _build")
+ shutil.rmtree('_build')
+ os.mkdir('_build')
+ shutil.copytree('.', '_build', symlinks=True,
+ ignore=shutil.ignore_patterns('_build'))
+ use_builddir = False
+ builddir = '.'
+ else:
+ builddir = '_build'
+
+ if not use_builddir:
+ configdir = './'
+ else:
+ configdir = os.getcwd()
+ builddir = builddir
+ log("Using build directory %r" % (builddir, ))
+ if not os.path.isdir(builddir):
+ os.mkdir(builddir)
+ os.chdir(builddir)
+
+ configstatus = 'config.status'
+ if not os.path.exists(configstatus):
+ args = [os.path.join(configdir, 'configure')]
+ args.extend(configargs)
+ run_sync(args)
+ else:
+ log("Found %s, skipping configure" % (configstatus, ))
+ phase_build()
+
+build_status = False
+
+def phase_build():
+ if not os.path.exists('Makefile'):
+ log("No Makefile found")
+ sys.exit(1)
+ args = makeargs
+ user_specified_jobs = False
+ for arg in args:
+ if arg == '-j':
+ user_specified_jobs = True
+
+ if not user_specified_jobs:
+ notparallel = _find_buildapi_makevariable('NOTPARALLEL')
+ if not notparallel:
+ log("Didn't find NOTPARALLEL, using parallel make by default")
+ args.extend(default_buildapi_jobs)
+
+ run_sync(args)
+
+def phase_complete():
+ sys.exit(0)
+
+log("invocation arguments: %r" % (sys.argv, ))
+
+# Start off the process
+phase_bootstrap()
--- /dev/null
+#!/usr/bin/python
+
+# ostree-buildone-raw: Generic build system wrapper
+# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
+# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
+
+import os,sys,re,subprocess
+import tempfile,shutil
+
+_devel_regexps = map(re.compile,
+ [r'/usr/include/',
+ r'/usr/share/pkgconfig/',
+ r'/.*lib(?:|(?:32)|(?:64))/pkgconfig/.*\.pc',
+ r'/.*lib(?:|(?:32)|(?:64))/.*\.so$'])
+
+def log(msg):
+ fullmsg = 'ostree-buildone: ' + msg + '\n'
+ sys.stdout.write(fullmsg)
+ sys.stdout.flush()
+
+tempfiles = []
+
+def do_exit(code):
+ for tmpname in tempfiles:
+ if os.path.isdir(tmpname):
+ shutil.rmtree(tmpname)
+ else:
+ try:
+ os.unlink(tmpname)
+ pass
+ except OSError, e:
+ pass
+ sys.exit(code)
+
+def fatal(msg):
+ log(msg)
+ do_exit(1)
+
+def run_sync(args, env=None):
+ log("Running: %r" % (args, ))
+ f = open('/dev/null', 'r')
+ proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
+ close_fds=True, env=env)
+ f.close()
+ returncode = proc.wait()
+ if returncode == 0:
+ func = log
+ else:
+ func = fatal
+ func("pid %d exited with code %d" % (proc.pid, returncode))
+
+basename=os.path.basename(os.getcwd())
+artifact_prefix='artifact-%s,%s' % (basename, os.environ['OSBUILD_VERSION'])
+origdir=os.getcwd()
+os.chdir('_build')
+
+if not os.path.exists('Makefile'):
+ log("No Makefile found")
+ do_exit(1)
+
+(fd,fakeroot_temp)=tempfile.mkstemp(prefix='ostree-fakeroot-%s-' % (basename,))
+os.close(fd)
+tempfiles.append(fakeroot_temp)
+tempdir = tempfile.mkdtemp(prefix='ostree-build-%s-' % (basename,))
+tempfiles.append(tempdir)
+args = ['fakeroot', '-s', fakeroot_temp, 'make', 'install', 'DESTDIR=' + tempdir]
+run_sync(args)
+
+devel_files = set()
+runtime_files = set()
+
+oldpwd=os.getcwd()
+os.chdir(tempdir)
+for root, dirs, files in os.walk('.'):
+ for filename in files:
+ path = os.path.join(root, filename)
+ matched = False
+ for r in _devel_regexps:
+ if r.match(path[1:]):
+ devel_files.add(path)
+ matched = True
+ break
+ if not matched:
+ runtime_files.add(path)
+os.chdir(oldpwd)
+
+def make_artifact(name, from_files):
+ artifact_target = '%s-%s.tar.gz' % (artifact_prefix, name)
+ (fd,filelist_temp)=tempfile.mkstemp(prefix='ostree-filelist-%s-%s' % (basename, name))
+ os.close(fd)
+ tempfiles.append(filelist_temp)
+ f = open(filelist_temp, 'w')
+ for filename in from_files:
+ f.write(filename)
+ f.write('\n')
+ f.close()
+ args = ['fakeroot', '-i', fakeroot_temp, 'tar', '-c', '-z', '-C', tempdir, '-f', artifact_target, '-T', filelist_temp]
+ run_sync(args)
+
+if devel_files:
+ make_artifact('devel', devel_files)
+make_artifact('runtime', runtime_files)
+
+do_exit(0)
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "ot-builtins.h"
+
+static OstreeBuiltin builtins[] = {
+ { "checkout", ostree_builtin_checkout, 0 },
+ { "diff", ostree_builtin_diff, 0 },
+ { "init", ostree_builtin_init, 0 },
+ { "commit", ostree_builtin_commit, 0 },
+ { "compose", ostree_builtin_compose, 0 },
+ { "log", ostree_builtin_log, 0 },
+#ifdef HAVE_LIBSOUP_GNOME
+ { "pull", ostree_builtin_pull, 0 },
+#endif
+ { "fsck", ostree_builtin_fsck, 0 },
+ { "remote", ostree_builtin_remote, 0 },
+ { "rev-parse", ostree_builtin_rev_parse, 0 },
+ { "remote", ostree_builtin_remote, 0 },
+ { "run-triggers", ostree_builtin_run_triggers, 0 },
+ { "show", ostree_builtin_show, 0 },
+ { NULL }
+};
+
+static int
+usage (char **argv, gboolean is_error)
+{
+ OstreeBuiltin *builtin = builtins;
+ void (*print_func) (const gchar *format, ...);
+
+ if (is_error)
+ print_func = g_printerr;
+ else
+ print_func = g_print;
+
+ print_func ("usage: %s --repo=PATH COMMAND [options]\n",
+ argv[0]);
+ print_func ("Builtin commands:\n");
+
+ while (builtin->name)
+ {
+ print_func (" %s\n", builtin->name);
+ builtin++;
+ }
+ return (is_error ? 1 : 0);
+}
+
+
+int
+main (int argc,
+ char **argv)
+{
+ OstreeBuiltin *builtin;
+ const char *cmd;
+ const char *repo;
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+
+ builtin = builtins;
+
+ if (argc < 3)
+ return usage (argv, 1);
+
+ if (!g_str_has_prefix (argv[1], "--repo="))
+ return usage (argv, 1);
+ repo = argv[1] + strlen ("--repo=");
+
+ cmd = argv[2];
+
+ while (builtin->name)
+ {
+ GError *error = NULL;
+ if (strcmp (cmd, builtin->name) == 0)
+ {
+ int i;
+ int tmp_argc;
+ char **tmp_argv;
+
+ tmp_argc = argc - 2;
+ tmp_argv = g_new0 (char *, tmp_argc + 1);
+
+ tmp_argv[0] = (char*)builtin->name;
+ for (i = 0; i < tmp_argc; i++)
+ tmp_argv[i+1] = argv[i+3];
+ if (!builtin->fn (tmp_argc, tmp_argv, repo, &error))
+ {
+ g_free (tmp_argv);
+ g_printerr ("%s\n", error->message);
+ g_clear_error (&error);
+ return 1;
+ }
+ g_free (tmp_argv);
+ return 0;
+ }
+ builtin++;
+ }
+
+ g_printerr ("Unknown command '%s'\n", cmd);
+ return usage (argv, 1);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+ { NULL }
+};
+
+gboolean
+ostree_builtin_checkout (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *commit;
+ const char *destination;
+ GFile *destf = NULL;
+
+ context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 3)
+ {
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "COMMIT and DESTINATION must be specified");
+ goto out;
+ }
+
+ commit = argv[1];
+ destination = argv[2];
+
+ if (!ostree_repo_checkout (repo, commit, destination, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ g_clear_object (&destf);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <gio/gunixoutputstream.h>
+
+#include <glib/gi18n.h>
+
+static gboolean separator_null;
+static int from_fd = -1;
+static gboolean from_stdin;
+static char *from_file;
+static char *metadata_text_path;
+static char *metadata_bin_path;
+static char *subject;
+static char *body;
+static char *parent;
+static char *branch;
+
+static GOptionEntry options[] = {
+ { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
+ { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
+ { "metadata-variant-text", 0, 0, G_OPTION_ARG_FILENAME, &metadata_text_path, "File containing g_variant_print() output", "path" },
+ { "metadata-variant", 0, 0, G_OPTION_ARG_FILENAME, &metadata_bin_path, "File containing serialized variant, in host endianness", "path" },
+ { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
+ { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
+ { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
+ { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
+ { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
+ { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
+ { NULL }
+};
+
+typedef struct {
+ GFile *dir;
+ char separator;
+ GOutputStream *out;
+ GCancellable *cancellable;
+} FindThreadData;
+
+static gboolean
+find (const char *basepath,
+ GFile *dir,
+ char separator,
+ GOutputStream *out,
+ GCancellable *cancellable,
+ GError **error);
+
+static gboolean
+find_write_child (const char *basepath,
+ GFile *dir,
+ char separator,
+ GOutputStream *out,
+ GFileInfo *finfo,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ guint32 type;
+ const char *name;
+ char buf[1];
+ char *child_path = NULL;
+ GString *child_trimmed_path = NULL;
+ GFile *child = NULL;
+ gsize bytes_written;
+
+ type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
+ name = g_file_info_get_attribute_byte_string (finfo, "standard::name");
+
+ child = g_file_get_child (dir, name);
+
+ if (type == G_FILE_TYPE_DIRECTORY)
+ {
+ if (!find (basepath, child, separator, out, cancellable, error))
+ goto out;
+ }
+
+ child_path = g_file_get_path (child);
+ child_trimmed_path = g_string_new (child_path + strlen (basepath));
+ if (!*(child_trimmed_path->str))
+ {
+ /* do nothing - we implicitly add the root . */
+ }
+ else
+ {
+ g_assert (*(child_trimmed_path->str) == '/');
+ g_string_insert_c (child_trimmed_path, 0, '.');
+
+ if (!g_output_stream_write_all (out, child_trimmed_path->str, child_trimmed_path->len,
+ &bytes_written, cancellable, error))
+ goto out;
+ buf[0] = separator;
+ if (!g_output_stream_write_all (out, buf, 1, &bytes_written,
+ cancellable, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_string_free (child_trimmed_path, TRUE);
+ child_trimmed_path = NULL;
+ g_free (child_path);
+ child_path = NULL;
+ g_clear_object (&child);
+ return ret;
+}
+
+static gboolean
+find (const char *basepath,
+ GFile *dir,
+ char separator,
+ GOutputStream *out,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ GFileEnumerator *enumerator = NULL;
+ GFileInfo *finfo = NULL;
+
+ enumerator = g_file_enumerate_children (dir, "standard::type,standard::name",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, error);
+ if (!enumerator)
+ goto out;
+
+ while ((finfo = g_file_enumerator_next_file (enumerator, cancellable, error)) != NULL)
+ {
+ if (!find_write_child (basepath, dir, separator, out, finfo, cancellable, error))
+ goto out;
+ g_clear_object (&finfo);
+ }
+ if (temp_error)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&finfo);
+ g_clear_object (&enumerator);
+ return ret;
+}
+
+static gpointer
+find_thread (gpointer data)
+{
+ FindThreadData *tdata = data;
+ GError *error = NULL;
+ char *path;
+
+ path = g_file_get_path (tdata->dir);
+ if (!find (path, tdata->dir, tdata->separator, tdata->out,
+ tdata->cancellable, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ g_free (path);
+ g_clear_object (&(tdata->dir));
+ g_clear_object (&(tdata->out));
+ return NULL;
+}
+
+gboolean
+ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ char *dir = NULL;
+ GChecksum *commit_checksum = NULL;
+ char separator;
+ GVariant *metadata = NULL;
+ GMappedFile *metadata_mappedf = NULL;
+ GFile *metadata_f = NULL;
+ gboolean temp_fd = -1;
+ int pipefd[2] = { -1, -1 };
+ GOutputStream *out = NULL;
+ FindThreadData fdata;
+
+ context = g_option_context_new ("[DIR] - Commit a new revision");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (argc > 1)
+ dir = g_strdup (argv[1]);
+ else
+ dir = g_get_current_dir ();
+
+ if (g_str_has_suffix (dir, "/"))
+ dir[strlen (dir) - 1] = '\0';
+
+ separator = separator_null ? '\0' : '\n';
+
+ if (!*dir)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty directory");
+ goto out;
+ }
+
+ if (metadata_text_path || metadata_bin_path)
+ {
+ metadata_mappedf = g_mapped_file_new (metadata_text_path ? metadata_text_path : metadata_bin_path, FALSE, error);
+ if (!metadata_mappedf)
+ goto out;
+ if (metadata_text_path)
+ {
+ metadata = g_variant_parse (G_VARIANT_TYPE ("a{sv}"),
+ g_mapped_file_get_contents (metadata_mappedf),
+ g_mapped_file_get_contents (metadata_mappedf) + g_mapped_file_get_length (metadata_mappedf),
+ NULL, error);
+ if (!metadata)
+ goto out;
+ }
+ else if (metadata_bin_path)
+ {
+ metadata_f = ot_util_new_file_for_path (metadata_bin_path);
+ if (!ot_util_variant_map (metadata_f, G_VARIANT_TYPE ("a{sv}"), &metadata, error))
+ goto out;
+ }
+ else
+ g_assert_not_reached ();
+ }
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (!branch)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A branch must be specified with --branch");
+ goto out;
+ }
+
+ if (!subject)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A subject must be specified with --subject");
+ goto out;
+ }
+
+ if (!(from_file || from_fd >= 0 || from_stdin))
+ {
+ /* We're using the current directory */
+
+ }
+
+ if (from_stdin)
+ from_fd = 0;
+ else if (from_file)
+ {
+ temp_fd = ot_util_open_file_read (from_file, error);
+ if (temp_fd < 0)
+ {
+ g_prefix_error (error, "Failed to open '%s': ", from_file);
+ goto out;
+ }
+ from_fd = temp_fd;
+ }
+ else
+ {
+ if (pipe (pipefd) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ out = (GOutputStream*)g_unix_output_stream_new (pipefd[1], TRUE);
+ from_fd = pipefd[0];
+
+ fdata.dir = ot_util_new_file_for_path (dir);
+ fdata.separator = separator;
+ fdata.out = out;
+ fdata.cancellable = NULL;
+
+ if (g_thread_create_full (find_thread, &fdata, 0, FALSE, FALSE, G_THREAD_PRIORITY_NORMAL, error) == NULL)
+ goto out;
+
+ out = NULL;
+ }
+
+ if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, metadata,
+ dir, from_fd, separator,
+ &commit_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ g_print ("%s\n", g_checksum_get_string (commit_checksum));
+ out:
+ g_clear_object (&out);
+ if (temp_fd >= 0)
+ (void)close (temp_fd);
+ if (pipefd[0] > 0)
+ (void) close (pipefd[0]);
+ if (pipefd[1] > 0)
+ (void) close (pipefd[1]);
+ g_free (dir);
+ if (metadata_mappedf)
+ g_mapped_file_unref (metadata_mappedf);
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ if (commit_checksum)
+ g_checksum_free (commit_checksum);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *compose_metadata_path;
+
+static GOptionEntry options[] = {
+ { "out-metadata", 0, 0, G_OPTION_ARG_FILENAME, &compose_metadata_path, "Output a file containing serialized metadata about the compose, in host endianness", "path" },
+ { NULL }
+};
+
+static void
+rm_rf (GFile *path)
+{
+ GFileInfo *finfo = NULL;
+ GFileEnumerator *path_enum = NULL;
+ guint32 type;
+
+ finfo = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, NULL);
+ if (!finfo)
+ goto out;
+
+ type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
+ if (type == G_FILE_TYPE_DIRECTORY)
+ {
+ path_enum = g_file_enumerate_children (path, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, NULL);
+ if (!path_enum)
+ goto out;
+
+
+ g_clear_object (&finfo);
+ while ((finfo = g_file_enumerator_next_file (path_enum, NULL, NULL)) != NULL)
+ {
+ GFile *child = g_file_get_child (path, g_file_info_get_attribute_byte_string (finfo, "standard::name"));
+ rm_rf (child);
+ g_clear_object (&child);
+ g_clear_object (&finfo);
+ }
+ }
+
+ (void) g_file_delete (path, NULL, NULL);
+
+ out:
+ g_clear_object (&finfo);
+ g_clear_object (&path_enum);
+}
+
+static gboolean
+merge_dir (GFile *destination,
+ GFile *src,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ char *src_path = NULL;
+ GError *temp_error = NULL;
+ GFileInfo *src_fileinfo = NULL;
+ GFileInfo *dest_fileinfo = NULL;
+ GFileEnumerator *src_enum = NULL;
+ GFile *dest_subfile = NULL;
+ GFile *src_subfile = NULL;
+ const char *name;
+ guint32 type;
+
+ dest_path = g_file_get_path (destination);
+ src_path = g_file_get_path (src);
+
+ dest_fileinfo = g_file_query_info (destination, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, &temp_error);
+ if (dest_fileinfo)
+ {
+ type = g_file_info_get_attribute_uint32 (dest_fileinfo, "standard::type");
+ if (type != G_FILE_TYPE_DIRECTORY)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Attempting to replace non-directory %s with directory %s",
+ dest_path, src_path);
+ goto out;
+ }
+
+ src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error);
+ if (!src_enum)
+ goto out;
+
+ while ((src_fileinfo = g_file_enumerator_next_file (src_enum, NULL, &temp_error)) != NULL)
+ {
+ type = g_file_info_get_attribute_uint32 (src_fileinfo, "standard::type");
+ name = g_file_info_get_attribute_byte_string (src_fileinfo, "standard::name");
+
+ dest_subfile = g_file_get_child (destination, name);
+ src_subfile = g_file_get_child (src, name);
+
+ if (type == G_FILE_TYPE_DIRECTORY)
+ {
+ if (!merge_dir (dest_subfile, src_subfile, error))
+ goto out;
+ }
+ else
+ {
+ if (!g_file_delete (dest_subfile, NULL, &temp_error))
+ {
+ if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ g_clear_error (&temp_error);
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+ if (!g_file_move (src_subfile, dest_subfile, 0, NULL, NULL, NULL, error))
+ goto out;
+ }
+
+ g_clear_object (&dest_subfile);
+ g_clear_object (&src_subfile);
+ }
+ if (temp_error)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+ else if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&temp_error);
+ if (!g_file_move (src, destination, 0, NULL, NULL, NULL, error))
+ goto out;
+ }
+ else
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ g_free (src_path);
+ g_clear_object (&src_fileinfo);
+ g_clear_object (&dest_fileinfo);
+ g_clear_object (&src_enum);
+ g_clear_object (&dest_subfile);
+ g_clear_object (&src_subfile);
+ return ret;
+}
+
+static gboolean
+compose_branch_on_dir (OstreeRepo *repo,
+ GFile *destination,
+ const char *branch,
+ GVariantBuilder *metadata_builder,
+ GError **error)
+{
+ char *destpath = NULL;
+ char *branchpath = NULL;
+ GFile *branchf = NULL;
+ GFileEnumerator *enumerator = NULL;
+ gboolean ret = FALSE;
+ char *branchrev = NULL;
+
+ if (!ostree_repo_resolve_rev (repo, branch, &branchrev, error))
+ goto out;
+
+ destpath = g_file_get_path (destination);
+ if (g_str_has_suffix (destpath, "/"))
+ destpath[strlen (destpath) - 1] = '\0';
+ branchpath = g_strconcat (destpath, "-tmp-checkout-", branchrev, NULL);
+ branchf = ot_util_new_file_for_path (branchpath);
+
+ g_print ("Checking out %s (commit %s)...\n", branch, branchrev);
+ if (!ostree_repo_checkout (repo, branchrev, branchpath, NULL, error))
+ goto out;
+ g_print ("...done\n");
+ g_print ("Merging over destination...\n");
+ if (!merge_dir (destination, branchf, error))
+ goto out;
+
+ if (metadata_builder)
+ g_variant_builder_add (metadata_builder, "(ss)", branch, branchrev);
+
+ ret = TRUE;
+ out:
+ if (branchf)
+ rm_rf (branchf);
+ g_clear_object (&enumerator);
+ g_clear_object (&branchf);
+ g_free (branchrev);
+ g_free (destpath);
+ g_free (branchpath);
+ return ret;
+}
+
+gboolean
+ostree_builtin_compose (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *destination;
+ GFile *destf = NULL;
+ gboolean compose_metadata_builder_initialized = FALSE;
+ GVariantBuilder compose_metadata_builder;
+ gboolean commit_metadata_builder_initialized = FALSE;
+ GVariantBuilder commit_metadata_builder;
+ GVariant *commit_metadata = NULL;
+ GFile *metadata_f = NULL;
+ int i;
+
+ context = g_option_context_new ("DESTINATION BRANCH1 BRANCH2 ... - Merge multiple commits into a single filesystem tree");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 3)
+ {
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "DESTINATION and at least one COMMIT must be specified");
+ goto out;
+ }
+
+ destination = argv[1];
+ destf = ot_util_new_file_for_path (destination);
+
+ if (compose_metadata_path)
+ {
+ compose_metadata_builder_initialized = TRUE;
+ g_variant_builder_init (&compose_metadata_builder, G_VARIANT_TYPE ("a(ss)"));
+ }
+
+ for (i = 2; i < argc; i++)
+ {
+ const char *branch = argv[i];
+
+ if (!compose_branch_on_dir (repo, destf, branch, compose_metadata_builder_initialized ? &compose_metadata_builder : NULL, error))
+ goto out;
+ }
+
+ if (compose_metadata_path)
+ {
+ commit_metadata_builder_initialized = TRUE;
+ g_variant_builder_init (&commit_metadata_builder, G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add (&commit_metadata_builder, "{sv}",
+ "ostree-compose", g_variant_builder_end (&compose_metadata_builder));
+ compose_metadata_builder_initialized = FALSE;
+
+ metadata_f = ot_util_new_file_for_path (compose_metadata_path);
+
+ commit_metadata = g_variant_builder_end (&commit_metadata_builder);
+ if (!ot_util_variant_save (metadata_f, commit_metadata, NULL, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (compose_metadata_builder_initialized)
+ g_variant_builder_clear (&compose_metadata_builder);
+ if (commit_metadata_builder_initialized)
+ g_variant_builder_clear (&commit_metadata_builder);
+ if (context)
+ g_option_context_free (context);
+ if (commit_metadata)
+ g_variant_unref (commit_metadata);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ g_clear_object (&destf);
+ g_clear_object (&metadata_f);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+ { NULL }
+};
+
+gboolean
+ostree_builtin_diff (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *target;
+ const char *rev;
+ GFile *targetf = NULL;
+ GPtrArray *modified = NULL;
+ GPtrArray *removed = NULL;
+ GPtrArray *added = NULL;
+
+ context = g_option_context_new ("REV TARGETDIR - Compare directory TARGETDIR against revision REV");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 3)
+ {
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "REV and TARGETDIR must be specified");
+ goto out;
+ }
+
+ rev = argv[1];
+ target = argv[2];
+ targetf = ot_util_new_file_for_path (target);
+
+ if (!ostree_repo_diff (repo, rev, targetf, &modified, &removed, &added, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&repo);
+ g_clear_object (&targetf);
+ if (modified)
+ g_ptr_array_free (modified, TRUE);
+ if (removed)
+ g_ptr_array_free (removed, TRUE);
+ if (added)
+ g_ptr_array_free (added, TRUE);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+ { NULL }
+};
+
+typedef struct {
+ guint n_objects;
+ gboolean had_error;
+} OtFsckData;
+
+static gboolean
+checksum_packed_file (OtFsckData *data,
+ const char *path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *ret_checksum = NULL;
+ GFile *file = NULL;
+ char *metadata_buf = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GFileInputStream *in = NULL;
+ guint32 metadata_len;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read;
+ char buf[8192];
+
+ file = ot_util_new_file_for_path (path);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+ goto out;
+
+ metadata_len = GUINT32_FROM_BE (metadata_len);
+
+ metadata_buf = g_malloc (metadata_len);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+ goto out;
+
+ metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE, NULL, NULL);
+
+ g_variant_get (metadata, "(uuuu@a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ &xattrs, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ do
+ {
+ if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+ goto out;
+ g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
+ }
+ while (bytes_read > 0);
+
+ ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+ g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+
+ ret = TRUE;
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_free (metadata_buf);
+ g_clear_object (&file);
+ g_clear_object (&in);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+static void
+object_iter_callback (OstreeRepo *repo,
+ const char *path,
+ GFileInfo *file_info,
+ gpointer user_data)
+{
+ OtFsckData *data = user_data;
+ struct stat stbuf;
+ GChecksum *checksum = NULL;
+ GError *error = NULL;
+ char *dirname = NULL;
+ char *checksum_prefix = NULL;
+ char *checksum_string = NULL;
+ char *filename_checksum = NULL;
+ gboolean packed = FALSE;
+ OstreeObjectType objtype;
+ char *dot;
+
+ /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
+ if (nlinks < 2 && !quiet)
+ g_printerr ("note: floating object: %s\n", path); */
+
+ if (g_str_has_suffix (path, ".meta"))
+ objtype = OSTREE_OBJECT_TYPE_META;
+ else if (g_str_has_suffix (path, ".file"))
+ objtype = OSTREE_OBJECT_TYPE_FILE;
+ else if (g_str_has_suffix (path, ".packfile"))
+ {
+ objtype = OSTREE_OBJECT_TYPE_FILE;
+ packed = TRUE;
+ }
+ else
+ g_assert_not_reached ();
+
+ if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ if (!checksum_packed_file (data, path, &checksum, &error))
+ goto out;
+ }
+ else
+ {
+ if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
+ goto out;
+ }
+
+ filename_checksum = g_strdup (g_file_info_get_name (file_info));
+ dot = strrchr (filename_checksum, '.');
+ g_assert (dot != NULL);
+ *dot = '\0';
+
+ dirname = g_path_get_dirname (path);
+ checksum_prefix = g_path_get_basename (dirname);
+ checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
+
+ if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
+ {
+ data->had_error = TRUE;
+ g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
+ path, g_checksum_get_string (checksum));
+ }
+
+ data->n_objects++;
+
+ out:
+ if (checksum != NULL)
+ g_checksum_free (checksum);
+ g_free (dirname);
+ g_free (checksum_prefix);
+ g_free (checksum_string);
+ g_free (filename_checksum);
+ if (error != NULL)
+ {
+ g_printerr ("%s\n", error->message);
+ g_clear_error (&error);
+ }
+}
+
+gboolean
+ostree_builtin_fsck (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ OtFsckData data;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+
+ context = g_option_context_new ("- Check the repository for consistency");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ data.n_objects = 0;
+ data.had_error = FALSE;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
+ goto out;
+
+ if (data.had_error)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered filesystem consistency errors");
+ goto out;
+ }
+ if (!quiet)
+ g_printerr ("Total Objects: %u\n", data.n_objects);
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean archive;
+
+static GOptionEntry options[] = {
+ { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
+ { NULL }
+};
+
+#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
+ "repo_version=0\n")
+
+
+gboolean
+ostree_builtin_init (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context = NULL;
+ gboolean ret = FALSE;
+ GFile *repodir = NULL;
+ GFile *child = NULL;
+ GFile *grandchild = NULL;
+ GString *config_data = NULL;
+
+ context = g_option_context_new ("- Initialize a new empty repository");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repodir = ot_util_new_file_for_path (repo_path);
+
+ child = g_file_get_child (repodir, "config");
+
+ config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
+ g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
+ if (!g_file_replace_contents (child,
+ config_data->str,
+ config_data->len,
+ NULL, FALSE, 0, NULL,
+ NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "objects");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "tmp");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "refs");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+
+ grandchild = g_file_get_child (child, "heads");
+ if (!g_file_make_directory (grandchild, NULL, error))
+ goto out;
+ g_clear_object (&grandchild);
+
+ grandchild = g_file_get_child (child, "remotes");
+ if (!g_file_make_directory (grandchild, NULL, error))
+ goto out;
+ g_clear_object (&grandchild);
+
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "tags");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ if (config_data)
+ g_string_free (config_data, TRUE);
+ g_clear_object (&repodir);
+ g_clear_object (&child);
+ g_clear_object (&grandchild);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+ { NULL }
+};
+
+gboolean
+ostree_builtin_log (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ GOutputStream *pager = NULL;
+ const char *rev;
+ GVariant *commit = NULL;
+ char *resolved_rev = NULL;
+
+ context = g_option_context_new ("- Show revision log");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (argc < 2)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A revision must be specified");
+ goto out;
+ }
+
+ rev = argv[1];
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (!ot_util_spawn_pager (&pager, error))
+ goto out;
+
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ while (TRUE)
+ {
+ OstreeSerializedVariantType type;
+ char *formatted = NULL;
+ guint32 version;
+ const char *parent;
+ const char *subject;
+ const char *body;
+ guint64 timestamp;
+ const char *contents;
+ const char *root_metadata;
+ GDateTime *time_obj = NULL;
+ char *formatted_date = NULL;
+ const char *body_newline;
+ gsize bytes_written;
+ GVariant *commit_metadata = NULL;
+ char *formatted_metadata = NULL;
+
+ if (commit)
+ g_variant_unref (commit);
+ if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
+ goto out;
+
+ /* Ignore commit metadata for now */
+ g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
+ &version, &commit_metadata, &parent, &subject, &body,
+ ×tamp, &contents, &root_metadata);
+ version = GUINT32_FROM_BE (version);
+ timestamp = GUINT64_FROM_BE (timestamp);
+ time_obj = g_date_time_new_from_unix_utc (timestamp);
+ formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
+ g_date_time_unref (time_obj);
+ time_obj = NULL;
+
+ formatted_metadata = g_variant_print (commit_metadata, TRUE);
+ g_variant_unref (commit_metadata);
+ formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
+ resolved_rev, subject, formatted_date, formatted_metadata);
+ g_free (formatted_metadata);
+ g_free (formatted_date);
+ formatted_date = NULL;
+
+ if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
+ {
+ g_free (formatted);
+ goto out;
+ }
+ g_free (formatted);
+
+ body_newline = strchr (body, '\n');
+ do {
+ gsize len;
+ if (!g_output_stream_write_all (pager, " ", 4, &bytes_written, NULL, error))
+ goto out;
+ len = body_newline ? body_newline - body : strlen (body);
+ if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
+ goto out;
+ body_newline = strchr (body, '\n');
+ if (!body_newline)
+ break;
+ else
+ body_newline += 1;
+ } while (*body_newline);
+
+ if (strcmp (parent, "") == 0)
+ break;
+ g_free (resolved_rev);
+ resolved_rev = g_strdup (parent);
+ }
+
+ if (!g_output_stream_close (pager, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (resolved_rev);
+ if (context)
+ g_option_context_free (context);
+ if (commit)
+ g_variant_unref (commit);
+ g_clear_object (&repo);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+#include <libsoup/soup-gnome.h>
+
+static GOptionEntry options[] = {
+ { NULL }
+};
+
+static gboolean
+fetch_uri (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *uri,
+ char **temp_filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ SoupMessage *msg = NULL;
+ guint response;
+ char *template = NULL;
+ int fd;
+ SoupBuffer *buf = NULL;
+ GFile *tempf = NULL;
+
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+
+ response = soup_session_send_message (soup, msg);
+ if (response != 200)
+ {
+ char *uri_string = soup_uri_to_string (uri, FALSE);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to retrieve '%s': %d %s",
+ uri_string, response, msg->reason_phrase);
+ g_free (uri_string);
+ goto out;
+ }
+
+ template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
+
+ fd = g_mkstemp (template);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ close (fd);
+ tempf = ot_util_new_file_for_path (template);
+
+ buf = soup_message_body_flatten (msg->response_body);
+
+ if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
+ goto out;
+
+ *temp_filename = template;
+ template = NULL;
+
+ ret = TRUE;
+ out:
+ g_free (template);
+ g_clear_object (&msg);
+ g_clear_object (&tempf);
+ return ret;
+}
+
+static gboolean
+store_object (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *baseuri,
+ const char *object,
+ OstreeObjectType objtype,
+ gboolean *did_exist,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *filename = NULL;
+ char *objpath = NULL;
+ char *relpath = NULL;
+ SoupURI *obj_uri = NULL;
+
+ objpath = ostree_get_relative_object_path (object, objtype, TRUE);
+ obj_uri = soup_uri_copy (baseuri);
+ relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
+ soup_uri_set_path (obj_uri, relpath);
+
+ if (!fetch_uri (repo, soup, obj_uri, &filename, error))
+ goto out;
+
+ if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (obj_uri)
+ soup_uri_free (obj_uri);
+ if (filename)
+ (void) unlink (filename);
+ g_free (filename);
+ g_free (objpath);
+ g_free (relpath);
+ return ret;
+}
+
+static gboolean
+store_tree_recurse (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *base_uri,
+ const char *rev,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *tree = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ OstreeSerializedVariantType metatype;
+ gboolean did_exist;
+ int i, n;
+
+ if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!did_exist)
+ {
+ if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
+ goto out;
+
+ if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Tree metadata '%s' has wrong type %d, expected %d",
+ rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
+ goto out;
+ }
+
+ /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+ g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
+ g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
+
+ n = g_variant_n_children (files_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *filename;
+ const char *checksum;
+
+ g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
+
+ if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
+ goto out;
+ }
+
+ n = g_variant_n_children (dirs_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *dirname;
+ const char *tree_checksum;
+ const char *meta_checksum;
+
+ g_variant_get_child (dirs_variant, i, "(sss)",
+ &dirname, &tree_checksum, &meta_checksum);
+
+ if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
+ goto out;
+
+ if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (tree)
+ g_variant_unref (tree);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static gboolean
+store_commit_recurse (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *base_uri,
+ const char *rev,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *commit = NULL;
+ OstreeSerializedVariantType metatype;
+ const char *tree_contents_checksum;
+ const char *tree_meta_checksum;
+ gboolean did_exist;
+
+ if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!did_exist)
+ {
+ if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
+ goto out;
+
+ if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Commit '%s' has wrong type %d, expected %d",
+ rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
+ goto out;
+ }
+
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+ g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+
+ if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ return ret;
+}
+
+gboolean
+ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *remote;
+ const char *branch;
+ char *remote_branch_ref_path = NULL;
+ char *key = NULL;
+ char *baseurl = NULL;
+ char *refpath = NULL;
+ char *temppath = NULL;
+ GKeyFile *config = NULL;
+ SoupURI *base_uri = NULL;
+ SoupURI *target_uri = NULL;
+ SoupSession *soup = NULL;
+ char *rev = NULL;
+
+ context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 3)
+ {
+ ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
+ goto out;
+ }
+
+ remote = argv[1];
+ branch = argv[2];
+
+ config = ostree_repo_get_config (repo);
+
+ key = g_strdup_printf ("remote \"%s\"", remote);
+ baseurl = g_key_file_get_string (config, key, "url", error);
+ if (!baseurl)
+ goto out;
+ base_uri = soup_uri_new (baseurl);
+ if (!base_uri)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to parse url '%s'", baseurl);
+ goto out;
+ }
+ target_uri = soup_uri_copy (base_uri);
+ g_free (refpath);
+ refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
+ soup_uri_set_path (target_uri, refpath);
+
+ soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+ NULL);
+ if (!fetch_uri (repo, soup, target_uri, &temppath, error))
+ goto out;
+
+ rev = ot_util_get_file_contents_utf8 (temppath, error);
+ if (!rev)
+ goto out;
+ g_strchomp (rev);
+
+ if (!ostree_validate_checksum_string (rev, error))
+ goto out;
+
+ if (!store_commit_recurse (repo, soup, base_uri, rev, error))
+ goto out;
+
+ if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ if (temppath)
+ (void) unlink (temppath);
+ g_free (temppath);
+ g_free (key);
+ g_free (rev);
+ g_free (baseurl);
+ g_free (refpath);
+ g_free (remote_branch_ref_path);
+ g_clear_object (&soup);
+ if (base_uri)
+ soup_uri_free (base_uri);
+ if (target_uri)
+ soup_uri_free (target_uri);
+ g_clear_object (&repo);
+ g_clear_object (&soup);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+ { NULL }
+};
+
+static void
+usage_error (GOptionContext *context, const char *message, GError **error)
+{
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ message);
+}
+
+gboolean
+ostree_builtin_remote (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *op;
+ GKeyFile *config = NULL;
+
+ context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 2)
+ {
+ usage_error (context, "OPERATION must be specified", error);
+ goto out;
+ }
+
+ op = argv[1];
+
+ config = ostree_repo_copy_config (repo);
+
+ if (!strcmp (op, "add"))
+ {
+ char *key;
+ if (argc < 4)
+ {
+ usage_error (context, "NAME and URL must be specified", error);
+ goto out;
+ }
+ key = g_strdup_printf ("remote \"%s\"", argv[2]);
+ g_key_file_set_string (config, key, "url", argv[3]);
+ g_free (key);
+ }
+ else
+ {
+ usage_error (context, "Unknown operation", error);
+ goto out;
+ }
+
+ if (!ostree_repo_write_config (repo, config, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ if (config)
+ g_key_file_free (config);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+ { NULL }
+};
+
+gboolean
+ostree_builtin_rev_parse (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *rev = "master";
+ char *resolved_rev = NULL;
+ GVariant *variant = NULL;
+ char *formatted_variant = NULL;
+
+ context = g_option_context_new ("REV - Output the target of a rev");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 2)
+ {
+ ot_util_usage_error (context, "REV must be specified", error);
+ goto out;
+ }
+ rev = argv[1];
+
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ g_print ("%s\n", resolved_rev);
+
+ ret = TRUE;
+ out:
+ g_free (resolved_rev);
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ if (variant)
+ g_variant_unref (variant);
+ g_free (formatted_variant);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_run_triggers (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *dir;
+
+ context = g_option_context_new ("DIR - Run trigger scripts for directory");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 1)
+ {
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "DIR must be specified");
+ goto out;
+ }
+
+ dir = argv[1];
+
+ checkout = ostree_checkout_new (repo, dir);
+ if (!ostree_checkout_run_triggers (checkout, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean print_compose;
+static char* print_variant_type;
+
+static GOptionEntry options[] = {
+ { "print-compose", 0, 0, G_OPTION_ARG_NONE, &print_compose, "If given, show the branches which make up the given compose commit", NULL },
+ { "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &print_variant_type, "If given, argument should be a filename and it will be interpreted as this type", NULL },
+ { NULL }
+};
+
+static void
+print_variant (GVariant *variant)
+{
+ char *formatted_variant = NULL;
+
+ formatted_variant = g_variant_print (variant, TRUE);
+ g_print ("%s\n", formatted_variant);
+
+ g_free (formatted_variant);
+}
+
+static gboolean
+do_print_variant_generic (const GVariantType *type,
+ const char *filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *f = NULL;
+ GVariant *variant = NULL;
+
+ f = ot_util_new_file_for_path (filename);
+
+ if (!ot_util_variant_map (f, type, &variant, error))
+ goto out;
+
+ print_variant (variant);
+
+ ret = TRUE;
+ out:
+ if (variant)
+ g_variant_unref (variant);
+ g_clear_object (&f);
+ return ret;
+}
+
+static gboolean
+show_repo_meta (OstreeRepo *repo,
+ const char *rev,
+ const char *resolved_rev,
+ GError **error)
+{
+ OstreeSerializedVariantType type;
+ gboolean ret = FALSE;
+ GVariant *variant = NULL;
+
+ if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
+ goto out;
+ g_print ("Object: %s\nType: %d\n", resolved_rev, type);
+ print_variant (variant);
+
+ ret = TRUE;
+ out:
+ if (variant)
+ g_variant_unref (variant);
+ return ret;
+}
+
+static gboolean
+do_print_compose (OstreeRepo *repo,
+ const char *rev,
+ const char *resolved_rev,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *variant = NULL;
+ GVariant *metadata = NULL;
+ GVariant *compose_contents = NULL;
+ GVariantIter *viter = NULL;
+ GHashTable *metadata_hash = NULL;
+ const char *branch;
+ const char *branchrev;
+
+ if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ resolved_rev, &variant, error))
+ goto out;
+
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ metadata = g_variant_get_child_value (variant, 1);
+ metadata_hash = ot_util_variant_asv_to_hash_table (metadata);
+
+ compose_contents = g_hash_table_lookup (metadata_hash, "ostree-compose");
+ if (!compose_contents)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Commit %s does not have compose metadata key \"ostree-compose\"", resolved_rev);
+ goto out;
+ }
+
+ g_variant_get_child (compose_contents, 0, "a(ss)", &viter);
+ while (g_variant_iter_next (viter, "(&s&s)", &branch, &branchrev))
+ {
+ g_print ("%s %s\n", branch, branchrev);
+ }
+
+ ret = TRUE;
+ out:
+ if (variant)
+ g_variant_unref (variant);
+ if (viter)
+ g_variant_iter_free (viter);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (metadata_hash)
+ g_hash_table_destroy (metadata_hash);
+ return ret;
+}
+
+gboolean
+ostree_builtin_show (int argc, char **argv, const char *repo_path, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *rev = "master";
+ char *resolved_rev = NULL;
+
+ context = g_option_context_new ("- Output a metadata object");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc > 1)
+ rev = argv[1];
+
+ if (print_compose)
+ {
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ if (!do_print_compose (repo, rev, resolved_rev, error))
+ goto out;
+ }
+ else if (print_variant_type)
+ {
+ if (!do_print_variant_generic (G_VARIANT_TYPE (print_variant_type), rev, error))
+ goto out;
+ }
+ else
+ {
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ if (!show_repo_meta (repo, rev, resolved_rev, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (resolved_rev);
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_BUILTINS__
+#define __OSTREE_BUILTINS__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ OSTREE_BUILTIN_FLAG_NONE = 0,
+} OstreeBuiltinFlags;
+
+typedef struct {
+ const char *name;
+ gboolean (*fn) (int argc, char **argv, const char *repo, GError **error);
+ int flags; /* OstreeBuiltinFlags */
+} OstreeBuiltin;
+
+gboolean ostree_builtin_checkout (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_commit (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_compose (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_diff (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_init (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_log (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_pull (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_fsck (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_show (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_remote (int argc, char **argv, const char *repo, GError **error);
+
+G_END_DECLS
+
+#endif